简介: 程序死锁的问题,很难调试,看进程堆栈,看各个线程与锁的情况,对照代码进行排查。数据库死锁的问题,更难,看不了数据库堆栈,也看不了数据库线程与锁,更难以对照代码排查。
程序死锁的问题,很难调试,看进程堆栈,看各个线程与锁的情况,对照代码进行排查。
数据库死锁的问题,更难,看不了数据库堆栈,也看不了数据库线程与锁,更难以对照代码排查。
前段时间,和一个朋友讨论了一个“疑似”数据库死锁的问题,最后进行试验与排查,找到了问题所在。
场景如下:
![2ed47cfe2ffea9a004b1336071cddd37.png](https://i-blog.csdnimg.cn/blog_migrate/27e3658dd97e57f49a2ea757433ea94e.jpeg)
同一个表,高并发事务,事务内先插入一条记录,再更新这条记录:
(1)如果更新的是唯一索引,有异常;
(2)如果更新的是自增主键,就没有异常;
画外音:先不要被“dead lock”描述所迷惑,是死锁问题,阻塞问题,还是其他异常,还另说。
![bacadb5358ba30f4961d31d720bc016e.png](https://i-blog.csdnimg.cn/blog_migrate/d8020cbe6263fe424b93e77d3c27e548.jpeg)
而且,据朋友所述,还能够复现:
(1)开启事务;
(2)插入记录;
(3)sleep 5秒;
(4)修改被插入的记录;
在并发时稳定复现。
根据朋友的描述,在线下开了多个MySQL客户端进行了并发模式测试,结果还挺出乎意料的。
第一步:数据准备
create table t (id int(20) primary key AUTO_INCREMENT,cell varchar(20) unique)engine=innodb;
新建表:
(1)存储引擎是innodb,MySQL版本是5.6;
(2)id字段,自增主键;
(3)cell字段,唯一索引;
start transaction;insert into t(cell) values(11111111111);insert into t(cell) values(22222222222);insert into t(cell) values(33333333333);commit;
插入一些测试数据。
第二步:session参数设置
事务的隔离级别,事务的自动提交等参数设置不当,都会对实验的结果产生影响,询问了朋友,事务的隔离级别是RR(repeatable read)。
set session autocommit=0;set session transaction isolation level repeatable read;
每一个session启动后:
(1)关闭自动提交;
(2)把事务隔离级别设为RR;
![4d3d85eaa8d66e3b2d8e239a9e89d6e3.png](https://i-blog.csdnimg.cn/blog_migrate/19b3eb638906b0f9dbf842bfb59e8b35.jpeg)
show session variables like "autocommit";show session variables like "tx_isolation";
不放心的话,可以用上面两个语句查询确认。
第三步:多个终端session模拟并发事务
![644b4b3271158b4c8859ff7bd7cef6e0.png](https://i-blog.csdnimg.cn/blog_migrate/84d86eaa83bae13643260b6b22b42959.jpeg)
如上图,用SecureCRT开启两个窗口:
(1)窗口A,先启动事务,并插入记录;
(2)窗口B,再启动事务,也插入记录;
(3)窗口A,修改插入的记录;
(4)窗口B,也修改插入的记录;
奇怪的现象发生了,如果并发事务的update语句:
(1)更新条件是cell,就会发生异常;
(2)更新条件是id,就一切正常;
按道理,插入不冲突的记录,然后修改这条记录,行锁不应该冲突呀?唯一索引,主键索引怎么会有差异呢?是否有关?是死锁,还是其他原因?
更多文章核心内容:点击下方“了解更多”
原文出处:阿里云大学开发者社区
![e700834fd8f4228c110bb967a69088ca.png](https://i-blog.csdnimg.cn/blog_migrate/7f14585823e68a3f1d4b0f3d6daad2e8.jpeg)