mysql5.7 为什么刚要死锁立刻就会报错而无需等待超时?

问题起因

自己做了个人造死锁的小实验:

Session1

Session2

Begin;

 

Select * from t where id=1 lock in share mode;

 

 

Begin;

 

Select * from t where id=1 lock in share mode;

 

Delete from t where id=1;//waiting

Delete from t where id=1;

//立刻报错:Deadlock found when trying to get lock; try restarting transaction

 

 

按我对mysql的理解,死锁出现之后应该慢慢等他超时然后回滚,结果这里刚敲了执行立刻错就报出来了,显然不是用的超时;那我盲猜mysql会保存每个session持有的资源,每次运行新的sql语句会把所有session以持有的资源扫描一遍看看有没有可能造成死锁。但是emmm,这个策略感觉效率会很低。

于是很好奇mysql用了什么检测机制。

答案结论

5.7版本开始,innodb_deadlock_detect开关变量被引入且默认开启。(可以用show variables like '%innodb_deadlock_detect%';查看)

于是mysql有了两种途径检测死锁:原来的等待超时和新增的死锁检测。

死锁检测的原理是构建一个以事务为顶点、锁为边的有向图,判断有向图是否存在环,存在即有死锁,所以如果同时存在大量事务,那么检测或许会非常非常慢。因此mysql对此进行了上限控制。如果运行的时候报错

TOO DEEP OR LONG SEARCH IN THE LOCK TABLE WAITS-FOR GRAPH, WE WILL ROLL BACK FOLLOWING TRANSACTION

那就表示目前正在等待的事务数量已达到200上限。超过200个事务的等待列表被视为死锁,并且尝试检查等待列表的事务被回滚。如果锁定线程必须查看等待列表中事务拥有的超过1,000,000个锁,也可能发生相同的错误。(详见MySQL5.7文档)

顺便,无需担心死锁检测机制的代码会发生死锁,因为它无需等待资源,并不具备死锁发生的条件

搜寻答案过程中找到的其他有趣point

innodb执行事务的完全回滚时,事务设置的所有锁都会被释放。但是,如果错误导致仅回滚一条sql语句,则该语句设置的一些锁可能会被保留。发生这种情况是因为innodb以这样一种格式存储行锁:它在以后无法知道哪个锁是由哪个语句设置的。

  1.  

 

 

 

你可能感兴趣的:(mysql深入)