MySQL死锁的调试
# 什么是死锁?
死锁是指由于每个事务都持有对方需要的锁而无法进行其他事务的情况。因为这两个事务都在等待资源变得可用,所以两个都不会释放它持有的锁。
数据库死锁,是最难调试与追踪的。所以本文记录一些死锁调试的思路。
# 死锁例子
以下示例说明了锁定请求将导致死锁时如何发生错误。该示例涉及两个事务A和B。
首先,创建一个包含一行的表,然后开始事务A。在事务中,A事务中对i=1的行加上S锁:
CREATE TABLE t (i INT) ENGINE = InnoDB;
INSERT INTO t (i) VALUES(1);
#A事务
START TRANSACTION;
SELECT * FROM t WHERE i = 1 FOR SHARE;
1
2
3
4
5
2
3
4
5
B事务删除 i=1 的行,删除操作需要一个X锁。无法授予该行的S锁,因为它与事务A持有的锁不兼容 ,因此事务B阻塞
#B事务
DELETE FROM t WHERE i = 1;
1
2
2
最后,事务A还尝试从表中删除该行:
#A事务
DELETE FROM t WHERE i = 1;
1
2
2
此处发生死锁,因为事务A需要 X锁才能删除该行。但是事务B已经有一个X锁定请求,并且正在等待客户端A释放其S锁定。所以进入了互相等待的死锁结果, InnoDB为其中一个客户端生成错误并释放其锁。客户端返回此错误:
ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction
1
2
2
因为MySQL的死锁检查机制,自动回滚影响最小的事务(事务中插入、更新或删除的行数确定),解除死锁。
# 死锁检测机制
InnoDB自动检测事务的死锁和回退事务解决死锁问题。 InnoDB尝试选择要回滚的小事务,其中事务的大小由插入,更新或删除的行数确定。
#查看事务阻塞时间
show VARIABLES like 'innodb_lock_wait_timeout'
#设置等待锁时间
SET innodb_lock_wait_timeout= some_number
#查看死锁是否自动回滚
show VARIABLES like 'innodb_deadlock_detect'
# 启动关闭死锁检测
SET innodb_deadlock_detect=0/1
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 处理死锁
# 查找死锁
- 最近一次死锁 在任何时候,发出以下命令以确定最近死锁的原因。然后调整代码以避免死锁。
SHOW ENGINE INNODB STATUS
1
- 所有死锁 通过启用innodb_print_all_deadlocks 配置选项来收集更广泛的调试信息 。有关每个死锁的信息,而不仅仅是最新的死锁,都记录在MySQL 错误日志中。完成调试后,请应该禁用此选项。
#查询是否启用所有死锁信息保存到日志
show variables like 'innodb_print_all_deadlocks';
set innodb_print_all_deadlocks=0/1;
1
2
3
2
3
# 定位死锁
比如上面那个死锁例子,通过SHOW ENGINE INNODB STATUS
命令拿到最近的死锁信息得到以下结果。
------------------------
LATEST DETECTED DEADLOCK
------------------------
2020-08-19 18:46:57 0x1ee0
*** (1) TRANSACTION:
TRANSACTION 87305, ACTIVE 3 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
MySQL thread id 9, OS thread handle 15096, query id 154 localhost ::1 root updating
DELETE FROM t WHERE i = 1
*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 163 page no 4 n bits 72 index GEN_CLUST_INDEX of table `area_data`.`t` trx id 87305 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 6; hex 000000000200; asc ;;
1: len 6; hex 000000015348; asc SH;;
2: len 7; hex 820000008b0110; asc ;;
3: len 4; hex 80000001; asc ;;
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 163 page no 4 n bits 72 index GEN_CLUST_INDEX of table `area_data`.`t` trx id 87305 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 6; hex 000000000200; asc ;;
1: len 6; hex 000000015348; asc SH;;
2: len 7; hex 820000008b0110; asc ;;
3: len 4; hex 80000001; asc ;;
*** (2) TRANSACTION:
TRANSACTION 87306, ACTIVE 6 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1136, 3 row lock(s)
MySQL thread id 8, OS thread handle 6104, query id 158 localhost ::1 root updating
DELETE FROM t WHERE i = 1
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 163 page no 4 n bits 72 index GEN_CLUST_INDEX of table `area_data`.`t` trx id 87306 lock mode S
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 6; hex 000000000200; asc ;;
1: len 6; hex 000000015348; asc SH;;
2: len 7; hex 820000008b0110; asc ;;
3: len 4; hex 80000001; asc ;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 163 page no 4 n bits 72 index GEN_CLUST_INDEX of table `area_data`.`t` trx id 87306 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 6; hex 000000000200; asc ;;
1: len 6; hex 000000015348; asc SH;;
2: len 7; hex 820000008b0110; asc ;;
3: len 4; hex 80000001; asc ;;
*** WE ROLL BACK TRANSACTION (1)
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
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
主题流程可以看到,事务2持有S锁,然后事务1请求X锁,然后事务2也去请求X锁,结果失败了执行回滚了
# 参考
在 GitHub 编辑此页 (opens new window)
上次更新: 2024/02/25, 12:11:11