Appearance
MySQL 死锁怎么排查:为什么会互相等,怎么从 SQL 和事务顺序上减少死锁
死锁是数据库并发场景里非常经典的问题。
它不是“数据库坏了”,而是多个事务的资源获取顺序冲突后,系统只能主动回滚其中一方。
先说结论
遇到死锁时,最重要的不是只会:
- 重试
而是要回头看三件事:
- 事务是不是太大
- 访问顺序是否不一致
- 更新条件是否走到了合适索引
很多死锁最终都能回到这三层解释。
一、死锁到底是什么
可以先粗略理解成:
- 事务 A 持有资源 1,等待资源 2
- 事务 B 持有资源 2,等待资源 1
双方都过不去。
这时数据库为了打破僵局,会选一个事务回滚。
二、为什么 MySQL 里经常出现死锁
最常见原因通常包括:
- 多事务更新同一批数据
- 多表更新顺序不一致
- 范围更新锁住了更大范围
- 事务执行时间过长
三、为什么索引命中情况很关键
很多人会把死锁只理解成“并发太高”,但实际上:
- 锁范围是不是过大
才是特别关键的问题。
如果更新没走到合适索引,就可能:
- 扫更大范围
- 锁更多记录
这会显著增加冲突概率。
四、排查死锁更实用的顺序
第一步:先看涉及哪些 SQL
确认到底是哪几类更新冲突最频繁。
第二步:看事务顺序是否一致
例如:
- 某些代码先更新表 A 再更新表 B
- 某些代码先更新表 B 再更新表 A
这就非常容易形成环路等待。
第三步:看事务是不是太大
事务里如果包含:
- 多步更新
- 外部调用
- 过长业务处理
锁持有时间就会放大。
第四步:看索引和锁范围
确认:
- 条件列是否命中索引
- 是否是范围更新
五、怎么减少死锁
1. 缩短事务
事务越短,冲突窗口越小。
2. 固定访问顺序
多资源更新时,统一顺序非常重要。
3. 尽量走索引更新
锁范围更小,冲突概率更低。
4. 把非必要逻辑移出事务
例如:
- RPC
- 慢 IO
- 非关键计算
六、为什么“完全避免死锁”不现实
在高并发系统里,死锁通常只能:
- 尽量减少
很难承诺绝不发生。
所以更实际的做法是:
- 设计上减少概率
- 应用层对死锁错误做可控重试
一句话总结
MySQL 死锁不是偶发现象,而是并发更新模型的一种自然结果。
真正有效的治理方式,通常不是“看到死锁就重试”,而是回到事务长度、访问顺序和索引路径这三层去收敛风险。