前言
近期在工作在遇到了几个和以前自己理解的一些不一样的死锁问题,因此想做一个mysql的死锁系列,梳理一下mysql引发死锁的一些场景,由于个人认知终归有限,本文所列的死锁场景不代表mysql全部的死锁触发原因,希望给其他同学启示,如能在工作中恰好起到帮助作用,那就更好了。
死锁是什么
如果有多个事务,这里我们暂定为两个,事务A需要请求锁lock_1,但是由于事务B占据着相关资源,导致事务A不能获得lock_1;同时,事务B请求锁lock_2,但是由于事务A占据着相关资源,导致事务B不能获得锁lock_2。为了不让两个事务都这么长期hang着,这个时候mysql将会根据自己的一套规则选出开销最小的事务,将其回滚,这样,另外一个事务就能够获取需要的锁资源继续执行了。
之前很多文章都将死锁的概念阐述为这样,这里简单描述就是:如果事务A已经持有锁lock_1,希望继续获取锁lock_2;同时,事务B已经获取锁lock_2,想继续获取锁lock_1,两个事务都占据着对方希望获取的锁,于是产生死锁!
如果按照第二种概念理解死锁的定义,核心思想就是死锁产生是因为双方事务想获取的锁都被对方事务占据着。这种理解其实有些片面,其实,死锁产生的原因本质上是两个事务已经持有的锁资源之间的相互影响,即只要事务B持有的锁影响了事务A对相关锁的获取,同时事务A持有的锁影响了事务B对相关锁的获取,无论事务A想获取的锁此时是否被事务B占据着,此时都形成死锁!
为什么要检测死锁
如果没有死锁检测机制,如果发生了上面的场景,mysql该怎么解决,是的,可能你想到了,可以使用锁超时,涉及相关的参数是innodb_lock_wait_timeout,但是这种方式是被动的,效率很低,相比之下,死锁检测就是更主动了,只要上面的说到的多事务互相hang住的场景一旦被mysql检测到,mysql就会主动回滚低开销事务。
死锁检测方法:
mysql采用wait-for graph 原理来检测死锁,简单的说就是多事务之间在互相hang住的时候,如果等待资源的矢量,形成了闭合回路,那么此时就判定为死锁,下图就是这样的例子:
事务1由于事务2已经持有的部分锁,导致不能获取相关的锁,矢量方向指向事务2。同理事务2,事务3,事务4,矢量方向最终在4个事物之间形成闭合环路,死锁形成。
请大家理解上面的概念,接下来的文章中我们将会用实际的例子来展现死锁产生的各种场景,在掌握了底层原理后,相信大家能以不变应万变