MySQL 死锁反复出现?一套从定位到根治的完整打法
生产环境数据库每隔几天报一次死锁,日志里多个事务交叉等待同一批表资源。很多同学的处置方式是调隔离级别、缩短事务,然后发现——还是死。这篇文章给一套完整的定位到根治路径,以 MySQL InnoDB 为例。
先说本质:死锁不是数据库的 bug,是两个事务以相反的顺序访问同一批资源。A 先锁行 r1 再要 r2,B 先锁 r2 再要 r1,互相等待,InnoDB 检测到环后回滚其中一方。调隔离级别和缩短事务只是降低概率,只要"相反顺序访问"这个结构性问题还在,死锁就一定会复发。
一、第一步:让数据库把现场交出来
很多人排查死锁失败,是因为死锁现场默认只保留最近一次,而且不留全量。先把证据链建好:
第一件事,打开全量死锁日志:
SET GLOBAL innodb_print_all_deadlocks = ON;
这样每个死锁都会写进 error log,而不是只留最后一次。事后诸葛亮和全量录像,是两种排查体验。
第二件事,拿最新一次死锁现场:
SHOW ENGINE INNODB STATUS
找 LATEST DETECTED DEADLOCK 段。里面有死锁发生瞬间两个事务各自正在执行的 SQL、各自持有的锁(HOLDS THE LOCK)、各自等待的锁(WAITING FOR)、锁的模式。这就是破案的核心证据。
第三件事,运行期间主动抓堵车现场:
SELECT * FROM information_schema.INNODB_TRX ORDER BY trx_started;
看当前有哪些长事务。再看:
SELECT * FROM sys.innodb_lock_waits;
这个视图直接告诉你"谁堵谁",输出里有 blocking_pid,拿着它去 PROCESSLIST 里查,就能看到冲突 SQL 原文。比翻日志快得多。
二、第二步:读死锁日志,关键是锁模式
拿到日志后,重点看三个东西。
第一个看 lock_mode。如果看到 locks rec but not gap,这是记录锁,说明两个事务按不同顺序更新同一批行,根治方向是统一访问顺序。如果看到 locks gap before rec 或者 insert intention waiting,这是间隙锁冲突,典型场景是 RR 隔离级别下范围 UPDATE 和 INSERT 打架,根治方向是收窄范围或者降隔离级别。如果看到表级 IX 锁和记录锁混合出现,多半是缺索引导致扫描范围放大
- 点赞
- 收藏
- 关注作者
评论(0)