客户在夜间批量执行数据处理时发生了死锁现象,是由不同的会话并发删除数据引起的,这个问题原因是比较简单,但想通过这个案例让大家熟悉如何去排查死锁问题,如何去阅读死锁日志这才是目的。通过模拟用户死锁现象后,死锁日志如下:
1*** (1) TRANSACTION:
2TRANSACTION 39474, ACTIVE 58 sec starting index read
3mysql tables in use 1, locked 1
4LOCK WAIT 3 lock struct(s), heap size 1200, 4 row lock(s), undo log entries 3
5MySQL thread id 9, OS thread handle 123145525800960, query id 77 localhost root updating
6DELETE FROM t1 WHERE id = 4
7*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
8RECORD LOCKS space id 114 page no 4 n bits 80 index PRIMARY of table `dhy`.`t1` trx id 39474 lock_mode X locks rec but not gap waiting
9Record lock, heap no 5 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
10 0: len 4; hex 00000004; asc ;;
11 1: len 6; hex 000000009a33; asc 3;;
12 2: len 7; hex 02000001471399; asc G ;;
13 3: len 2; hex 6464; asc dd;;
14
15*** (2) TRANSACTION:
16TRANSACTION 39475, ACTIVE 46 sec starting index read
17mysql tables in use 1, locked 1
183 lock struct(s), heap size 1200, 4 row lock(s), undo log entries 3
19MySQL thread id 10, OS thread handle 123145526104064, query id 78 localhost root updating
20DELETE FROM t1 WHERE id = 3
21*** (2) HOLDS THE LOCK(S):
22RECORD LOCKS space id 114 page no 4 n bits 80 index PRIMARY of table `dhy`.`t1` trx id 39475 lock_mode X locks rec but not gap
23Record lock, heap no 5 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
24 0: len 4; hex 00000004; asc ;;
25 1: len 6; hex 000000009a33; asc 3;;
26 2: len 7; hex 02000001471399; asc G ;;
27 3: len 2; hex 6464; asc dd;;
28
29Record lock, heap no 6 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
30 0: len 4; hex 00000005; asc ;;
31 1: len 6; hex 000000009a33; asc 3;;
32 2: len 7; hex 02000001471375; asc G u;;
33 3: len 2; hex 6565; asc ee;;
34
35Record lock, heap no 7 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
36 0: len 4; hex 00000006; asc ;;
37 1: len 6; hex 000000009a33; asc 3;;
38 2: len 7; hex 02000001471351; asc G Q;;
39 3: len 2; hex 6666; asc ff;;
40
41*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
42RECORD LOCKS space id 114 page no 4 n bits 80 index PRIMARY of table `dhy`.`t1` trx id 39475 lock_mode X locks rec but not gap waiting
43Record lock, heap no 4 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
44 0: len 4; hex 00000003; asc ;;
45 1: len 6; hex 000000009a32; asc 2;;
46 2: len 7; hex 01000001462e1f; asc F. ;;
47 3: len 2; hex 6363; asc cc;;
48
49*** WE ROLL BACK TRANSACTION (2)
要排查死锁问题我们就要学会如何查看死锁日志,但MySQL死锁日志看起来并不是很直观需要我们一步一步耐心分析。
我们将上面的死锁日志拆分阅读,我们可以得出以下信息:
两个事务的事务ID
TRANSACTION 39474
TRANSACTION 39475
事务39474在执行delete语句是发生了锁等待
1DELETE FROM t1 WHERE id = 4
2*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
3RECORD LOCKS space id 114 page no 4 n bits 80 index PRIMARY of table `dhy`.`t1` trx id 39474 lock_mode X locks rec but not gap waiting
4Record lock, heap no 5 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
5 0: len 4; hex 00000004; asc ;; //聚集索引的值
6 1: len 6; hex 000000009a33; asc 3;; //事务ID
7 2: len 7; hex 02000001471399; asc G ;; //undo 记录
8 3: len 2; hex 6464; asc dd;; //非主键字段的值
通过以上信息可以得出事务39474执行delete语句时,锁等待发生在申请ID=4这条记录上的X锁“lock_mode X locks rec but not gap waiting”
事务39475持有锁的信息
1*** (2) HOLDS THE LOCK(S):
2RECORD LOCKS space id 114 page no 4 n bits 80 index PRIMARY of table `dhy`.`t1` trx id 39475 lock_mode X locks rec but not gap
3Record lock, heap no 5 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
4 0: len 4; hex 00000004; asc ;;
5 1: len 6; hex 000000009a33; asc 3;;
6 2: len 7; hex 02000001471399; asc G ;;
7 3: len 2; hex 6464; asc dd;;
8
9Record lock, heap no 6 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
10 0: len 4; hex 00000005; asc ;;
11 1: len 6; hex 000000009a33; asc 3;;
12 2: len 7; hex 02000001471375; asc G u;;
13 3: len 2; hex 6565; asc ee;;
14
15Record lock, heap no 7 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
16 0: len 4; hex 00000006; asc ;;
17 1: len 6; hex 000000009a33; asc 3;;
18 2: len 7; hex 02000001471351; asc G Q;;
19 3: len 2; hex 6666; asc ff;;
事务39475持有在ID=4,5,6上X锁
事务39475同样在执行delete语句时发生了所等待
1*** (2) TRANSACTION:
2TRANSACTION 39475, ACTIVE 46 sec starting index read
3mysql tables in use 1, locked 1
43 lock struct(s), heap size 1200, 4 row lock(s), undo log entries 3
5MySQL thread id 10, OS thread handle 123145526104064, query id 78 localhost root updating
6DELETE FROM t1 WHERE id = 3
7
8*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
9RECORD LOCKS space id 114 page no 4 n bits 80 index PRIMARY of table `dhy`.`t1` trx id 39475 lock_mode X locks rec but not gap waiting
10Record lock, heap no 4 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
11 0: len 4; hex 00000003; asc ;;
12 1: len 6; hex 000000009a32; asc 2;;
13 2: len 7; hex 01000001462e1f; asc F. ;;
14 3: len 2; hex 6363; asc cc;;
申请ID=3上的X锁时发生了所等待,执行的语句是:DELETE FROM t1 WHERE id = 3,那么可以得出39474在id=3上持有了X锁,但是在死锁日志中并没有显示出事务39474持有锁的信息
那么这两个事务加锁的顺序应是:
1. 事务39474持有了id=3上的X锁
2. 事务39475持有了id=4上的X锁
3. 事务39474申请id=4上X锁时发生了锁等待执行的语句是:DELETE FROM t1 WHERE id = 4
4. 事务39475申请id=3上X锁时触发了死锁,因为此时双方都在申请对方持有的锁不能进行下去了。
事务2被回滚
1*** WE ROLL BACK TRANSACTION (2)
事务39475持有ID = 4, 5, 6上的X锁是由哪个语句引起的,无法直观从死锁日志里看出。可以通过打开general日志或者binlog或者业务代码来查看整个事务逻辑
搭建可按实验步骤自己模拟测试,表结构及数据如下:
1CREATE TABLE t1 (id int unsigned NOT NULL PRIMARY KEY, c1
2varchar(10));
3INSERT INTO t1 VALUES (1, 'aa'), (2, 'bb'), (3, 'cc'), (4, 'dd'), (5, 'ee'), (6, 'ff');
操作步骤如表所示:
Session1 | Session2 |
---|---|
START TRANSACTION; DELETE FROM t1 WHERE id = 1; |
|
START TRANSACTION; DELETE FROM t1 WHERE id = 6; |
|
DELETE FROM t1 WHERE id = 2; | |
DELETE FROM t1 WHERE id = 5; | |
DELETE FROM t1 WHERE id = 3; | |
DELETE FROM t1 WHERE id = 4; | |
DELETE FROM t1 WHERE id = 4; | |
DELETE FROM t1 WHERE id = 3; //发生死锁 |
这个案例根本原因是两个会话同时删除数据时,没有控制好删除的顺序造成了死锁,这就需要我们在做应用开发时对数据库操作一定要注意操作数据的前后关系、是否有数据依赖、会话之间是否会操作相同的数据。
通过这个案例我们也了解到了应如何去阅读和分析死锁日志。
《深入浅出MGR》视频课程
戳此小程序即可直达B站
https://www.bilibili.com/medialist/play/1363850082?business=space_collection&business_id=343928&desc=0
文章推荐:
MySQL内存为什么不断增高,怎么让它释放
MySQL 存储过程运行的内存管理
无损半同步复制下,主从切换后数据一致吗?
数据中间件如何与MySQL数据同步?
MySQL 8.0.30,一个值得上车MGR的版本
RC隔离级别下,死锁案例分析
浅析MySQL死锁检测
MySQL内存管理机制浅析
想看更多技术好文,点个“在看”吧!