MySQL InnoDB死锁日志深度解析
·
〇.给定的死锁日志片段
2025-09-24T09:11:00.008513Z 0 [Note] [MY-012468] [InnoDB] Transactions deadlock detected, dumping detailed information.
2025-09-24T09:11:00.008577Z 0 [Note] [MY-012469] [InnoDB] *** (1) TRANSACTION:
TRANSACTION 14616638, ACTIVE 55 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 8, OS thread handle 140565638231808, query id 81 localhost root updating
delete from errlog_test where id=2
2025-09-24T09:11:00.008609Z 0 [Note] [MY-012469] [InnoDB] *** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 84 page no 4 n bits 72 index PRIMARY of table `maria`.`errlog_test` trx id 14616638 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
0: len 4; hex 80000001; asc ;;
1: len 6; hex 000000df083e; asc >;;
2: len 7; hex 0100000367249d; asc g$ ;;
3: len 1; hex 61; asc a;;
2025-09-24T09:11:00.008710Z 0 [Note] [MY-012469] [InnoDB] *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 84 page no 4 n bits 72 index PRIMARY of table `maria`.`errlog_test` trx id 14616638 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
0: len 4; hex 80000002; asc ;;
1: len 6; hex 000000df083f; asc ?;;
2: len 7; hex 0200000176278e; asc v' ;;
3: len 1; hex 62; asc b;;
2025-09-24T09:11:00.008766Z 0 [Note] [MY-012469] [InnoDB] *** (2) TRANSACTION:
TRANSACTION 14616639, ACTIVE 11 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 11, OS thread handle 140565638027008, query id 82 localhost root updating
delete from errlog_test where id=1
2025-09-24T09:11:00.008781Z 0 [Note] [MY-012469] [InnoDB] *** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 84 page no 4 n bits 72 index PRIMARY of table `maria`.`errlog_test` trx id 14616639 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
0: len 4; hex 80000002; asc ;;
1: len 6; hex 000000df083f; asc ?;;
2: len 7; hex 0200000176278e; asc v' ;;
3: len 1; hex 62; asc b;;
2025-09-24T09:11:00.008832Z 0 [Note] [MY-012469] [InnoDB] *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 84 page no 4 n bits 72 index PRIMARY of table `maria`.`errlog_test` trx id 14616639 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
0: len 4; hex 80000001; asc ;;
1: len 6; hex 000000df083e; asc >;;
2: len 7; hex 0100000367249d; asc g$ ;;
3: len 1; hex 61; asc a;;
2025-09-24T09:11:00.008886Z 0 [Note] [MY-012469] [InnoDB] *** WE ROLL BACK TRANSACTION (2)
要理解给定的日志片段,首先需明确其本质——MySQL数据库InnoDB引擎的死锁日志。死锁是指两个或多个事务互相持有对方所需的锁,导致永久等待的异常场景。本文将从「日志解读方法」「具体场景拆解」「解决方案落地」三个维度,系统讲解死锁日志的分析逻辑,帮你快速定位问题、规避风险。
一、InnoDB死锁日志:核心结构与关键字段解读
InnoDB死锁日志遵循固定的输出逻辑,按「事件概览→事务1详情→事务2详情→解决结果」的顺序记录。掌握以下核心字段的含义,是读懂日志的关键:
| 日志模块 | 关键字段/关键词 | 含义解释 |
|---|---|---|
| 1. 死锁事件标识 | TRANSACTIONS deadlock detected | 明确标记“检测到死锁”,后续将输出详细信息 |
| 2. 事务基础信息 | TRANSACTION XXX | 事务唯一ID(如日志中的14616638),用于区分不同事务 |
ACTIVE X sec | 事务已活跃的时间(活跃越久,对数据库性能影响越大,可能暗示事务未及时提交) | |
MySQL thread id | 执行事务的数据库线程ID(可通过show processlist;命令定位该线程状态) | |
| 日志末尾的SQL语句 | 事务当前执行的SQL(直接触发死锁的操作,如delete from errlog_test where id=2) | |
| 3. 事务持有锁信息 | HOLDS THE LOCK(S) | 标识该事务已成功获取的锁列表 |
lock_mode X | 锁类型为「排他锁(X锁)」:MySQL中写操作(DELETE/UPDATE/INSERT)会自动加X锁,加锁后其他事务无法再获取该锁 | |
locks rec but not gap | 锁粒度为「行级锁(非间隙锁)」:仅锁定具体某一行数据,不包含行之间的间隙 | |
index PRIMARY | 锁加在「主键索引」上:InnoDB主键索引即聚簇索引,锁定主键等价于锁定整行数据 | |
table maria.errlog_test | 锁对应的数据库(maria)与表(errlog_test) | |
heap no X | 数据行在物理页中的位置(可理解为“行编号”,如heap no 2即第2行数据) | |
hex 80000001(首个字段) | 主键字段的十六进制值(如日志中80000001转换为十进制后为1,即行的id=1) | |
| 4. 事务等待锁信息 | WAITING FOR THIS LOCK TO BE GRANTED | 标识该事务正在等待的锁(此锁被其他事务持有,导致当前事务阻塞) |
| 5. 死锁解决结果 | WE ROLL BACK TRANSACTION (X) | MySQL自动选择回滚某一个事务(通常优先回滚“代价更小”的事务,如活跃时间短的),打破死锁僵局 |
二、具体日志拆解:还原死锁发生的完整过程
结合上述字段解读规则,我们逐段分析日志,还原两个事务的锁竞争场景。
1. 死锁事件概览
2025-09-24T09:11:00.008513Z 0 [Note] [MY-012468] [InnoDB] Transactions deadlock detected, dumping detailed information.
- 关键信息:2025年9月24日09:11:00,InnoDB引擎检测到死锁,开始输出详细日志。
- 作用:明确死锁发生的时间点,为后续定位业务操作提供时间锚点。
2. 事务1(TRANSACTION 14616638):持有id=1的锁,等待id=2的锁
① 事务基础信息
2025-09-24T09:11:00.008577Z 0 [Note] [MY-012469] [InnoDB] *** (1) TRANSACTION:
TRANSACTION 14616638, ACTIVE 55 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 8, OS thread handle 140565638231808, query id 81 localhost root updating
delete from errlog_test where id=2
- 核心结论:
- 事务ID:14616638,已活跃55秒(活跃时间较长,需警惕“事务未及时提交”的问题);
- 当前执行SQL:删除
errlog_test表中id=2的行; - 执行线程:线程ID为8(可通过
show processlist;查看该线程是否仍在阻塞)。
② 事务1已持有的锁
2025-09-24T09:11:00.008609Z 0 [Note] [MY-012469] [InnoDB] *** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 84 page no 4 n bits 72 index PRIMARY of table `maria`.`errlog_test` trx id 14616638 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
0: len 4; hex 80000001; asc ;; # 主键id=1(十六进制80000001转十进制为1)
1: len 6; hex 000000df083e; asc >;; # 事务ID相关信息
2: len 7; hex 0100000367249d; asc g$ ;; # 行数据版本信息
3: len 1; hex 61; asc a;; # 其他字段值(此处为字段值"a")
- 核心结论:事务1已成功获取
errlog_test表中「id=1行」的排他锁(X锁) ,此时该行为事务1独占,其他事务无法对其加X锁。
③ 事务1正在等待的锁
2025-09-24T09:11:00.008710Z 0 [Note] [MY-012469] [InnoDB] *** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 84 page no 4 n bits 72 index PRIMARY of table `maria`.`errlog_test` trx id 14616638 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
0: len 4; hex 80000002; asc ;; # 主键id=2(十六进制80000002转十进制为2)
1: len 6; hex 000000df083f; asc ?;;
2: len 7; hex 0200000176278e; asc v' ;;
3: len 1; hex 62; asc b;; # 其他字段值(此处为字段值"b")
- 核心结论:事务1要执行
delete from errlog_test where id=2,需获取「id=2行」的排他锁,但该锁已被其他事务持有,因此事务1进入阻塞等待状态。
3. 事务2(TRANSACTION 14616639):持有id=2的锁,等待id=1的锁
① 事务基础信息
2025-09-24T09:11:00.008766Z 0 [Note] [MY-012469] [InnoDB] *** (2) TRANSACTION:
TRANSACTION 14616639, ACTIVE 11 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 11, OS thread handle 140565638027008, query id 82 localhost root updating
delete from errlog_test where id=1
- 核心结论:
- 事务ID:14616639,已活跃11秒(活跃时间短于事务1);
- 当前执行SQL:删除
errlog_test表中id=1的行; - 执行线程:线程ID为11。
② 事务2已持有的锁
2025-09-24T09:11:00.008781Z 0 [Note] [MY-012469] [InnoDB] *** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 84 page no 4 n bits 72 index PRIMARY of table `maria`.`errlog_test` trx id 14616639 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
0: len 4; hex 80000002; asc ;; # 主键id=2
1: len 6; hex 000000df083f; asc ?;;
2: len 7; hex 0200000176278e; asc v' ;;
3: len 1; hex 62; asc b;;
- 核心结论:事务2已成功获取
errlog_test表中「id=2行」的排他锁(X锁) ——这正是事务1等待的锁。
③ 事务2正在等待的锁
2025-09-24T09:11:00.008832Z 0 [Note] [MY-012469] [InnoDB] *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 84 page no 4 n bits 72 index PRIMARY of table `maria`.`errlog_test` trx id 14616639 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
0: len 4; hex 80000001; asc ;; # 主键id=1
1: len 6; hex 000000df083e; asc >;;
2: len 7; hex 0100000367249d; asc g$ ;;
3: len 1; hex 61; asc a;;
- 核心结论:事务2要执行
delete from errlog_test where id=1,需获取「id=1行」的排他锁,但该锁已被事务1持有,因此事务2也进入阻塞等待状态。
4. 死锁解决结果
2025-09-24T09:11:00.008886Z 0 [Note] [MY-012469] [InnoDB] *** WE ROLL BACK TRANSACTION (2)
- 核心结论:MySQL自动选择回滚「事务2」(因事务2活跃时间更短,回滚代价更小),释放其持有的「
id=2行」排他锁,让事务1可继续执行,最终打破死锁。
三、死锁根源与解决方案:从规避到优化
1. 死锁的根本原因
本次死锁的核心是「交叉加锁顺序」:两个事务对同一批数据的加锁顺序相反,形成闭环等待:
- 事务1:先加锁
id=1→ 再请求id=2的锁; - 事务2:先加锁
id=2→ 再请求id=1的锁; - 双方互相持有对方所需的锁,且均不释放,最终触发死锁。
这种场景常见于“多步写操作”的业务中(如批量删除、批量更新),若不同事务的操作顺序不统一,极易引发锁竞争。
2. 落地级解决方案
针对“交叉加锁顺序”类死锁,可从「统一加锁逻辑」「优化事务设计」「监控预警」三个层面解决:
(1)统一所有事务的加锁顺序
核心原则:所有涉及同一表多行写操作的事务,均按「主键/唯一索引升序」或「固定规则」加锁,避免交叉请求。
示例:若业务需删除id=1和id=2的行,强制所有事务按“先小后大”的顺序执行SQL:
-- 正确:先删id=1,再删id=2(所有事务统一此顺序)
DELETE FROM errlog_test WHERE id=1;
DELETE FROM errlog_test WHERE id=2;
-- 错误:不同事务按相反顺序执行(如事务1先删id=1,事务2先删id=2)
(2)缩短事务时长,减少锁持有时间
日志中事务1活跃55秒,可能是“事务未及时提交”(如执行SQL后未调用COMMIT),导致锁长期被占用。
优化建议:
- 事务内仅包含必要的SQL操作,避免冗余逻辑(如避免在事务中调用外部接口、打印大量日志);
- 执行完写操作后立即提交事务,避免手动延迟或遗忘
COMMIT; - 若业务允许,将大事务拆分为小事务(如批量删除拆分为单次删除100行,减少锁持有范围)。
(3)增加死锁监控与重试机制
- 监控:通过MySQL的
information_schema.INNODB_LOCKS和INNODB_LOCK_WAITS表,实时查看锁持有与等待情况;或借助Prometheus+Grafana监控Innodb_deadlocks指标(死锁发生次数),及时预警。 - 重试:在业务代码中增加“死锁重试逻辑”,若捕获到
Deadlock found when trying to get lock异常,自动重试13次(重试间隔建议10100ms),降低业务影响。
总结回顾
InnoDB死锁日志的分析核心是“找锁的持有与等待关系”:先通过关键字段定位每个事务的锁状态,再还原加锁顺序,最终找到死锁根源。而解决死锁的关键在于“统一加锁顺序+缩短事务时长”——前者从源头避免锁竞争,后者降低锁冲突的概率。
掌握死锁日志的解读能力,不仅能快速定位线上问题,更能帮助我们在业务设计阶段规避潜在风险,提升数据库的稳定性。
更多推荐
所有评论(0)