死锁案例之二 Myth丶恋晨 2022-12-01 05:11 166阅读 0赞 ### 来源:公众号yangyidba ### **一 前言** 死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA都会在工作过程中遇见。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助。本文源于我们的生产案例:并发申请gap锁导致的死锁案例,与之前的 死锁案例一不同,本案例是因为RR模式下两个事务中的sql可以获取同一个gap锁,导致对方事务的insert 相互等待,导致死锁的。 **二 案例分析** 2.1 测试环境准备 Percona server 5.6.24 事务隔离级别为RR > 1. CREATE TABLE \`t4\` ( > > 2. \`id\` bigint(20) unsigned NOT NULL AUTO\_INCREMENT , > > 3. \`kdt\_id\` int(11) unsigned NOT NULL , > > 4. \`admin\_id\` int(11) unsigned NOT NULL , > > 5. \`biz\` varchar(20) NOT NULL DEFAULT '1' , > > 6. \`role\_id\` int(11) unsigned NOT NULL , > > 7. \`shop\_id\` int(11) unsigned NOT NULL DEFAULT '0' , > > 8. \`operator\` varchar(20) NOT NULL DEFAULT '0' , > > 9. \`operator\_id\` int(11) NOT NULL DEFAULT '0' , > > 10. \`create\_time\` datetime NOT NULL DEFAULT CURRENT\_TIMESTAMP COMMENT '创建时间', > > 11. \`update\_time\` datetime NOT NULL DEFAULT CURRENT\_TIMESTAMP COMMENT '更新时间', > > 12. PRIMARY KEY (\`id\`), > > 13. UNIQUE KEY \`uniq\_kid\_aid\_biz\_rid\` (\`kdt\_id\`,\`admin\_id\`,\`role\_id\`,\`biz\`) > > 14. ) ENGINE=InnoDB AUTO\_INCREMENT=1 DEFAULT CHARSET=utf8; > 15. INSERT INTO \`t4\` (\`id\`, \`kdt\_id\`, \`admin\_id\`, \`biz\`, \`role\_id\`, \`shop\_id\`, \`operator\`, \`operator\_id\`, \`create\_time\`, \`update\_time\`) > > 16. VALUES > > 17. (1,10,1,'retail',1,0,'0',0,'2017-05-09 15:55:26','2017-05-09 15:55:26'), > > 18. (2,20,1,'retail',1,0,'0',0,'2017-05-09 15:55:40','2017-05-09 15:55:40'), > > 19. (3,30,1,'retail',1,0,'0',0,'2017-05-09 15:55:55','2017-05-09 15:55:55'), > > 20. (4,40,1,'retail',1,0,'0',0,'2017-05-09 15:56:06','2017-05-09 15:56:06'), > > 21. (5,50,1,'retail',1,0,'0',0,'2017-05-09 15:56:16','2017-05-09 15:56:16'); 2.2 本测试案例场景是两个事务删除不存的行,然后在insert记录。 <table> <tbody> <tr> <td>T2</td> <td>T1</td> </tr> <tr> <td>test [RW] 02:50:27 >begin;<br>Query OK, 0 rows affected (0.00 sec)</td> <td>test [RW] 02:50:27 >begin;<br>Query OK, 0 rows affected (0.00 sec)</td> </tr> <tr> <td>test [RW] 02:50:34 >delete from t4 where kdt_id = 15 and admin_id = 1 <br>and biz = 'retail' and role_id = '1';</td> <td><br></td> </tr> <tr> <td><br></td> <td>test [RW] 02:50:41 >delete from t4 where kdt_id = 18 and admin_id = 2 and biz = 'retail' and role_id = '1';</td> </tr> <tr> <td><br></td> <td>test [RW] 02:50:43 >insert into t4(`kdt_id`, `admin_id`, `biz`, `role_id`, `shop_id`, `operator`, `operator_id`, `create_time`, `update_time`)<br> -> VALUES('18', '2', 'retail', '2', '0', '0', '0', CURRENT_TIMESTAMP,CURRENT_TIMESTAMP);</td> </tr> <tr> <td>test [RW] 02:51:02 >INSERT INTO t4(`kdt_id`, `admin_id`, `biz`, `role_id`, `shop_id`, `operator`, `operator_id`, `create_time`, `update_time`)<br> -> VALUES ('15', '1', 'retail', '2', '0', '0', '0', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);<br>ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction</td> <td><br></td> </tr> </tbody> </table> **2.3 死锁日志** 1. \------------------------ 2. LATEST DETECTED DEADLOCK 3. \------------------------ 4. 2017-09-11 14:51:03 7f78eaf25700 5. \*\*\* (1) TRANSACTION: 6. TRANSACTION 462308535, ACTIVE 20 sec inserting 7. mysql tables in use 1, locked 1 8. LOCK WAIT 3 lock struct(s), heap size 360, 2 row lock(s), undo log entries 1 9. MySQL thread id 3584515, OS thread handle 0x7f78ea5f5700, query id 780258123 localhost root update 10. insert into t4(\`kdt\_id\`, \`admin\_id\`, \`biz\`, \`role\_id\`, \`shop\_id\`, \`operator\`, \`operator\_id\`, \`create\_time\`, \`update\_time\`) 11. VALUES('18', '2', 'retail', '2', '0', '0', '0', CURRENT\_TIMESTAMP, CURRENT\_TIMESTAMP) 12. \*\*\* (1) WAITING FOR THIS LOCK TO BE GRANTED: 13. RECORD LOCKS space id 225 page no 4 n bits 72 index \`uniq\_kid\_aid\_biz\_rid\` of table \`test\`.\`t4\` trx id 462308535 lock\_mode X locks gap before rec insert intention waiting 14. \*\*\* (2) TRANSACTION: 15. TRANSACTION 462308534, ACTIVE 29 sec inserting, thread declared inside InnoDB 5000 16. mysql tables in use 1, locked 1 17. 3 lock struct(s), heap size 360, 2 row lock(s), undo log entries 1 18. MySQL thread id 3584572, OS thread handle 0x7f78eaf25700, query id 780258153 localhost root update 19. INSERT INTO t4(\`kdt\_id\`, \`admin\_id\`, \`biz\`, \`role\_id\`, \`shop\_id\`, \`operator\`, \`operator\_id\`, \`create\_time\`, \`update\_time\`) 20. VALUES ('15', '1', 'retail', '2', '0', '0', '0', CURRENT\_TIMESTAMP, CURRENT\_TIMESTAMP) 21. \*\*\* (2) HOLDS THE LOCK(S): 22. RECORD LOCKS space id 225 page no 4 n bits 72 index \`uniq\_kid\_aid\_biz\_rid\` of table \`test\`.\`t4\` trx id 462308534 lock\_mode X locks gap before rec 23. \*\*\* (2) WAITING FOR THIS LOCK TO BE GRANTED: 24. RECORD LOCKS space id 225 page no 4 n bits 72 index \`uniq\_kid\_aid\_biz\_rid\` of table \`test\`.\`t4\` trx id 462308534 lock\_mode X locks gap before rec insert intention waiting 25. \*\*\* WE ROLL BACK TRANSACTION (2) **2.4 死锁日志分析 **首先根据《[死锁案例一][Link 1]》 和《一个最不可思议的MySQL死锁分析》中强调 delete 不存在的记录是要加上GAP锁,事务日志中显示Lock\_mode X wait . a. T2 delete from t4 where kdt\_id = 15 and admin\_id = 1 and biz = 'retail' and role\_id = '1'; 符合条件的记录不存在,导致T2 先持有了(lock\_mode X locks gap before rec) 锁住\[(2,20,1,'retail',1,0)-(3,30,1,'retail',1,0)\]的区间 ,防止符合条件的记录插入。 b. T1的delete 与T2的delete一样 同样申请了 (lock\_mode X locks gap before rec) 锁住\[(2,20,1,'retail',1,0)-(3,30,1,'retail',1,0)\]的区间 。 > 1. It is also worth noting here that conflicting locks can be held on a gap by different transactions. For example, transaction A can hold a shared gap lock (gap S-lock) on a gap while transaction B holds an exclusive gap lock (gap X-lock) on the same gap. The reason conflicting gap locks are allowed is that if a record is purged from an index, the gap locks held on the record by different transactions must be merged. c. T1 的insert 语句申请插入意向锁,但是插入意向锁和T2持有的X GAP (lock\_mode X locks gap before rec) 冲突,故等待T2中的GAP 锁释放。 > 1. Gap locks in InnoDB are “purely inhibitive”, which means they only stop other transactions from inserting to the gap. They do not prevent different transactions from taking gap locks on the same gap. Thus, a gap X-lock has the same effect as a gap S-lock. d. T2 的insert 语句申请插入意向锁,但是插入意向锁和T1持有 X GAP (lock\_mode X locks gap before rec) 冲突,故等待T1中的GAP 锁释放。 T1(INSERT )等待T2(DELETE),T2(INSERT)等待T1(DELETE) 故而循环等待,出现死锁。 有兴趣的读者朋友可以测试一下 delete 存在记录的场景。 **2.6 如何解决呢?** a 先select 检查一下看看是否存在,然后再删除。这里也存在两个或者多个会话并发执行同一个select where条件的,这里需要开发同学做处理。 b 使用insert into on deuplicate key语法不存在则插入,而不是先删除,再插入。 **三 小结** RR事务隔离级别和GAP锁是导致死锁的常见原因,但是业务逻辑设计不合理也会出发死锁,本文的案例通过修改业务逻辑最终将死锁解决。 全文完。 Enjoy MySQL :) -------------------- 叶老师的「MySQL核心优化」大课已升级到MySQL 8.0,扫码开启MySQL 8.0修行之旅吧 ![format_png][] [Link 1]: http://mp.weixin.qq.com/s?__biz=MzI4NjExMDA4NQ%3D%3D&chksm=f3c97decc4bef4fa593987d6b25d54d918b7eb54a45a526026b5432bf9842666a25fa6163638&idx=1&mid=2648450310&scene=21&sn=4ea2c6c77f0971efa18bae38cb74a082#wechat_redirect [format_png]: /images/20221123/ca21568356de4b5091e7a7dfd8f5e259.png
相关 死锁案例 死锁成因 了解了innodb锁的基本原理后,下面分析下死锁的成因。如前面所说,死锁一般是事务相互等待对方资源,最后形成环路造成的。下面简单讲下造成相互等待 r囧r小猫/ 2023年01月05日 04:00/ 0 赞/ 237 阅读
相关 死锁案例之八 来源:公众号yangyidba 一 前言 死锁其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发朋友都会在工作过程中遇见。关于死锁我会持续写一个系列的 Dear 丶/ 2022年12月17日 08:41/ 0 赞/ 149 阅读
相关 死锁案例 六 一、前言 死锁,其实是一个很有意思也很有挑战的技术问题,大概每个 DBA 和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁 Myth丶恋晨/ 2022年12月13日 01:29/ 0 赞/ 212 阅读
相关 死锁案例六 来源:公众号yangyidba 一、前言 死锁,其实是一个很有意思也很有挑战的技术问题,大概每个 DBA 和部分开发同学都会在工作过程中遇见 。关于死 迷南。/ 2022年12月10日 11:26/ 0 赞/ 192 阅读
相关 死锁案例 二 一 前言 死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA都会在工作过程中遇见。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋友有所帮助 深碍√TFBOYSˉ_/ 2022年12月08日 01:44/ 0 赞/ 141 阅读
相关 死锁 二 一、前言 每个MySQL DBA和开发大概率都会遇到死锁问题,本文是自己对死锁相关知识总结,介绍死锁是什么,MySQL如何检测死锁/处理死锁,死锁的案例,以及如何避免死锁 不念不忘少年蓝@/ 2022年12月08日 01:43/ 0 赞/ 157 阅读
相关 死锁案例之四 来源:公众号yangyidba 一、前言 死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写一个系列 左手的ㄟ右手/ 2022年12月04日 10:58/ 0 赞/ 152 阅读
相关 死锁案例之二 来源:公众号yangyidba 一 前言 死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA都会在工作过程中遇见。关于死锁我会持续写一个 Myth丶恋晨/ 2022年12月01日 05:11/ 0 赞/ 167 阅读
相关 死锁案例一 来源:公众号yangyidba 一、前言 死锁,其实是一个很有意思也很有挑战的技术问题,大概每个 DBA 和部分开发同学都会在工作过程中遇见 。关于死锁我会持 电玩女神/ 2022年11月29日 12:42/ 0 赞/ 230 阅读
相关 死锁案例之九 来源:公众号yangyidba 一 前言 死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写 曾经终败给现在/ 2022年11月21日 03:51/ 0 赞/ 188 阅读
还没有评论,来说两句吧...