死锁主因是索引未建对或用错,where条件未走索引导致行锁退化为全表扫描;explain中type为all或index须优化,避免隐式类型转换和函数包裹索引字段。

死锁不是配置调出来的,而是SQL锁不准酿成的——90% 的高并发死锁,根子在索引没建对或用错了。
WHERE 条件没走索引 → 行锁退化成全表扫描
这是最隐蔽也最致命的锁扩大源头。哪怕你只更新一行,只要 WHERE 条件没命中索引,InnoDB 就会全表扫描,对每行加意向锁甚至行锁,其他事务立刻排队等这把“本不该存在”的锁。
- 用
EXPLAIN查type字段:出现ALL或index就必须停手优化 - 避免隐式类型转换:
WHERE user_id = '123'(user_id是INT)会失效索引,改成WHERE user_id = 123 - 别用函数包裹索引字段:
WHERE DATE(create_time) = '2026-05-01'→ 改成WHERE create_time >= '2026-05-01' AND create_time - 联合索引注意最左前缀:建了
(status, category),但只查WHERE category = 'A',照样全表扫
SELECT FOR UPDATE / ON DUPLICATE KEY UPDATE 锁范围失控
这两个语句看似原子,实际内部都依赖“快速定位”。一旦定位不准,就会在查找阶段误加大量间隙锁(Gap Lock),导致插入新记录也被阻塞,甚至引发死锁。
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
-
SELECT * FROM orders WHERE order_no = 'ORD-123' FOR UPDATE:如果order_no有唯一索引 → 只加记录锁,安全 -
SELECT * FROM orders WHERE user_id = 1001 FOR UPDATE:若user_id无索引 → 全表扫描 + 每行加锁 → 实际退化为表级竞争 -
INSERT INTO orders (...) ON DUPLICATE KEY UPDATE ...:必须确保ON DUPLICATE KEY依赖的字段(如order_no)有唯一索引,否则查找阶段会锁住整个可能插入的间隙 - 多个唯一索引(如
UNIQUE(email)和UNIQUE(phone))并发冲突时,InnoDB 内部加锁顺序不一致,也可能死锁
UPDATE/DELETE 语句锁得不准 → 死锁温床
死锁不是凭空来的。两个事务更新同一组数据,但一个按主键升序加锁,另一个按降序或无序加锁,再加个间隙锁,环形等待立刻成型。
-
ORDER BY id ASC,保证加锁顺序全局一致 - 避免
WHERE status IN (1,2)这类范围条件——它会触发Next-Key Lock(记录锁 + 间隙锁),锁住整个索引区间 - 用
EXPLAIN FORMAT=JSON看used_range_optimizer_pruning和range_analysis,确认是否真的用了索引做范围裁剪
真正难的不是建索引,而是让每条 FOR UPDATE、UPDATE、INSERT ... ON DUPLICATE KEY UPDATE 都能精准命中唯一索引或覆盖索引——漏掉一条,就可能成为死锁链上的那个断点。










