WHERE字段无索引会导致全表扫描并加next-key lock,大量无关行被锁,引发高并发更新卡死;必须为查询条件列建立合适索引,避免函数操作、优化索引顺序,并定期清理无效索引。

为什么 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 会在插入过程中对整个聚簇索引间隙加锁,多个事务争抢同一间隙时极易死锁。
系统功能强大、操作便捷并具有高度延续开发的内容与知识管理系统,并可集合系统强大的新闻、产品、下载、人才、留言、搜索引擎优化、等功能模块,为企业部门提供一个简单、易用、开放、可扩展的企业信息门户平台或电子商务运行平台。开发人员为脆弱页面专门设计了防刷新系统,自动阻止恶意访问和攻击;安全检查应用于每一处代码中,每个提交到系统查询语句中的变量都经过过滤,可自动屏蔽恶意攻击代码,从而全面防止SQL注入攻击
- 必须为
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 很低的,优先排查是否冗余
索引不是越多越好,而是让每个锁尽可能窄、每次查找尽可能准。很多并发问题表面是锁等待,根子在索引没切中查询的真实边界。