为什么 WHERE
条件字段没加索引会导致并发更新卡死
高并发下执行
UPDATE t SET status = 1 WHERE user_id = 123,如果
user_id没有索引,MySQL 会走全表扫描,每行都加
next-key lock(间隙锁 + 行锁),导致大量无关行被锁住,其他事务一碰就
Lock wait timeout exceeded。这不是性能慢,是直接阻塞。 必须确保所有
WHERE、
JOIN、
ORDER BY、
GROUP BY中出现的列,至少被某个联合索引的最左前缀覆盖 用
EXPLAIN FORMAT=TREE看执行计划,确认
key列非
NULL,且
rows_examined接近匹配行数,而非表总行数 避免在索引列上做函数操作,比如
WHERE DATE(create_time) = '2024-01-01'会让索引失效;改用
WHERE create_time >= '2024-01-01' AND create_time
联合索引字段顺序怎么排才不浪费锁范围
并发更新常带多个条件,比如
UPDATE order SET paid_at = NOW() WHERE user_id = 1001 AND status = 'unpaid' AND created_at > '2024-05-01'。索引顺序错,可能让锁住的间隙远超必要范围。 等值查询字段放最左(如
user_id),因为 B+ 树能快速定位到具体子树 范围查询字段(如
created_at > ...)必须放最后,否则后续字段无法被索引使用
status是低基数字段(只有 'unpaid'/'paid'/'cancelled'),放在中间或末尾都行,但别放最左——否则索引区分度太低,优化器可能弃用 推荐索引:
INDEX idx_user_status_created (user_id, status, created_at)
唯一性约束和 INSERT ... ON DUPLICATE KEY UPDATE
怎么避免死锁
秒杀场景常用
INSERT INTO stock (item_id, qty) VALUES (101, -1) ON DUPLICATE KEY UPDATE qty = qty - 1,若
item_id只有主键、没单独建
UNIQUE INDEX,MySQL 会在插入过程中对整个聚簇索引间隙加锁,多个事务争抢同一间隙时极易死锁。 必须为
ON DUPLICATE KEY的判断字段建立
UNIQUE或
PRIMARY KEY约束,否则无法精准定位冲突行,锁范围不可控 避免在同一个语句里同时更新多个唯一键字段,例如
ON DUPLICATE KEY UPDATE a = VALUES(a), b = VALUES(b)若
a和
b都是唯一键,会触发多次唯一性检查和锁 高频并发写入时,可考虑把唯一约束从
UNIQUE KEY (a,b)拆成
UNIQUE KEY (a)+ 应用层校验
b,减少索引维护开销
什么时候该删索引而不是加索引
每多一个索引,
INSERT/UPDATE/DELETE就得多维护一份 B+ 树,尤其在写多读少的并发场景下,索引可能成为瓶颈本身。常见误判是“反正加了不慢,留着备用”。 用
SELECT * FROM sys.schema_unused_indexes(MySQL 8.0+)或解析
performance_schema.table_io_waits_summary_by_index_usage找长期未被使用的索引 删除前确认:该索引是否只被
ORDER BY或
GROUP BY单独使用?如果是,且对应 SQL 并发量极低,大概率可删 特别警惕
prefix index(如
INDEX idx_name (name(10))):如果实际查询中经常用
WHERE name = 'xxxlongstring',前 10 字符重复率高,这个索引几乎无效,还拖慢写入
SHOW INDEX FROM orders; -- 看 Key_name、Seq_in_index、Cardinality 列 -- Seq_in_index=1 且 Cardinality 很低的,优先排查是否冗余
索引不是越多越好,而是让每个锁尽可能窄、每次查找尽可能准。很多并发问题表面是锁等待,根子在索引没切中查询的真实边界。
