§ 死锁排查分析


本文介绍在GreatSQL数据库中,如何查看死锁信息以及分析死锁发生的可能原因。

§ 1. 关于死锁

之所以会发生死锁,是因为锁等待产生了循环回路,也就是死结。

例如,事务A持有数据R1上的锁L1,事务B持有数据R2上的锁L2;紧接着,事务A继续请求数据R2上的锁,事务A会被L2阻塞;事务B继续请求数据R1上的锁,这时就产生了循环回路,触发死锁检测,其中有个事务会失败被回滚。

时间线 事务A 事务B
t1 begin; begin;
t2 select * from t1 where id=1 for update;
t3 select * from t1 where id=2 for update;
t4 select * from t1 where id=2 for update;
被阻塞
t5 select * from t1 where id=1 for update;
触发死锁检测,报告死锁,失败回滚事务
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

在InnoDB中,有个死锁检测的后台线程,当检测到死锁后,会马上抛出异常并回滚一个事务(另一个继续执行),InnoDB选择回滚代价较小(锁定或修改的行数较少)的事务,如果两个事务代价一样,则选择启动时间较晚的那个事务。

选项 innodb_deadlock_detect 用于设置是否启用死锁检测,默认打开。

在有大量高并发事务场景中,开启死锁检测可能会造成系统变慢,因为有可能大量事务在等待同一个锁。此时可以考虑关闭死锁检测 innodb_deadlock_detect=0,或者调低行锁等待阈值 innodb_lock_wait_timeout

§ 2. 查看死锁日志

以上述死锁场景为例,产生的死锁日志如下:

------------------------
LATEST DETECTED DEADLOCK
------------------------

# 死锁发生时间
2023-09-07 16:45:13 139836574922496

# 发生死锁的第一个事务A
*** (1) TRANSACTION:
TRANSACTION 5315754291, ACTIVE 10 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s)
MySQL thread id 13338, OS thread handle 139836330592000, query id 36923052144 localhost root statistics
select * from t1 where id=2 for update

# 事务A持有的行锁(id=1上的Record Lock)
*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 688 page no 4 n bits 80 index PRIMARY of table `greatsql`.`t1` trx id 5315754291 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000001; asc     ;;
 1: len 6; hex 00013cd7f90c; asc   <   ;;
 2: len 7; hex 82000010360110; asc     6  ;;
 3: len 4; hex 80021d42; asc    B;;
 4: len 4; hex 000aee94; asc     ;;


# 事务A在等待的行锁(id=2上的Record Lock)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 688 page no 4 n bits 80 index PRIMARY of table `greatsql`.`t1` trx id 5315754291 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 00013cd7f90c; asc   <   ;;
 2: len 7; hex 8200001036011d; asc     6  ;;
 3: len 4; hex 8007ba9d; asc     ;;
 4: len 4; hex 000146d3; asc   F ;;


# 发生死锁的第二个事务B
*** (2) TRANSACTION:
TRANSACTION 5315754292, ACTIVE 5 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1128, 2 row lock(s)
MySQL thread id 13339, OS thread handle 139836355770112, query id 36923052145 localhost root statistics
select * from t1 where id=1 for update

# 事务B持有的行锁(id=2上的Record Lock)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 688 page no 4 n bits 80 index PRIMARY of table `greatsql`.`t1` trx id 5315754292 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000002; asc     ;;
 1: len 6; hex 00013cd7f90c; asc   <   ;;
 2: len 7; hex 8200001036011d; asc     6  ;;
 3: len 4; hex 8007ba9d; asc     ;;
 4: len 4; hex 000146d3; asc   F ;;


# 事务B在等待的行锁(id=1上的Record Lock)
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 688 page no 4 n bits 80 index PRIMARY of table `greatsql`.`t1` trx id 5315754292 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000001; asc     ;;
 1: len 6; hex 00013cd7f90c; asc   <   ;;
 2: len 7; hex 82000010360110; asc     6  ;;
 3: len 4; hex 80021d42; asc    B;;
 4: len 4; hex 000aee94; asc     ;;

# 回滚第二个事务B(两个事务代价一样,但事务B启动时间更晚)
*** WE ROLL BACK TRANSACTION (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

SHOW ENGINE INNODB STATUS 输出结果中,只能保存最后一次死锁事件,如果想要记录全部死锁事件,需要设置 innodb_print_all_deadlocks=1,就会在选项 log_error 指向的日志文件中,记录所有死锁事件。

§ 3. 查看分析行锁等待

事实上,如果只看死锁日志的话,是很难直接分析出死锁发生的原因,通常要采用推测模拟的方式来复现死锁场景。或者观察死锁发生的规律,在容易发生死锁的时间段里设置 general_log=1,记录那个时段的所有请求,在死锁发生后再关闭该选项,然后结合死锁日志,分析那个时段的请求日志,判断死锁产生原因。

参考资料

§ 问题反馈

§ 联系我们

扫码关注微信公众号

greatsql-wx