来自:贝壳DBA
咱们使用MySQL大概率上都会遇到死锁问题,这实在是个令人非常头痛的问题。本文将会对死锁进行相应介绍,对常见的死锁案例进行相关分析与探讨,以及如何去尽可能避免死锁给出一些建议。
--什么是死锁--
死锁是并发系统中常见的问题,同样也会出现在数据库MySQL的并发读写请求场景中。当两个及以上的事务,双方都在等待对方释放已经持有的锁或因为加锁顺序不一致造成循环等待锁资源,就会出现“死锁”。常见的报错信息为”Deadlockfoundwhentryingtogetlock...”。
举例来说A事务持有X1锁,申请X2锁,B事务持有X2锁,申请X1锁。A和B事务持有锁并且申请对方持有的锁进入循环等待,就造成了死锁。
如上图,是右侧的四辆汽车资源请求产生了回路现象,即死循环,导致了死锁。
从死锁的定义来看,MySQL出现死锁的几个要素为:
a.两个或者两个以上事务
b.每个事务都已经持有锁并且申请新的锁
c.锁资源同时只能被同一个事务持有或者不兼容
d.事务之间因为持有锁和申请锁导致彼此循环等待
说明:后续内容实验环境为5.7版本,隔离级别为RR(可重复读)
--InnoDB锁类型--
为了分析死锁,我们有必要对InnoDB的锁类型有一个了解。
MySQLInnoDB引擎实现了标准的行级别锁:共享锁(Slock)和排他锁(Xlock)
不同事务可以同时对同一行记录加S锁。如果一个事务对某一行记录加X锁,其他事务就不能加S锁或者X锁,从而导致锁等待。如果事务T1持有行r的S锁,那么另一个事务T2请求r的锁时,会做如下处理:
T2请求S锁立即被允许,结果T1T2都持有r行的S锁T2请求X锁不能被立即允许如果T1持有r的X锁,那么T2请求r的X、S锁都不能被立即允许,T2必须等待T1释放X锁才可以,因为X锁与任何的锁都不兼容。共享锁和排他锁的兼容性如下所示:
间隙锁(gaplock)
间隙锁锁住一个间隙以防止插入。假设索引列有2,4,8三个值,如果对4加锁,那么也会同时对(2,4)和(4,8)这两个间隙加锁。其他事务无法插入索引值在这两个间隙之间的记录。但是,间隙锁有个例外:
如果索引列是唯一索引,那么只会锁住这条记录(只加行锁),而不会锁住间隙。对于联合索引且是唯一索引,如果where条件只包括联合索引的一部分,那么依然会加间隙锁。next-keylock
next-keylock实际上就是行锁+这条记录前面的gaplock的组合。假设有索引值10,11,13和20,那么可能的next-keylock包括:
(负无穷,10]
(10,11]
(11,13]
(13,20]
(20,正无穷)
在RR隔离级别下,InnoDB使用next-keylock主要是防止幻读问题产生。
意向锁(Intentionlock)
InnoDB为了支持多粒度的加锁,允许行锁和表锁同时存在。为了支持在不同粒度上的加锁操作,InnoDB支持了额外的一种锁方式,称之为意向锁(IntentionLock)。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度上进行加锁。意向锁分为两种:
意向共享锁(IS):事务有意向对表中的某些行加共享锁意向排他锁(IX):事务有意向对表中的某些行加排他锁由于InnoDB存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫描以外的任何请求。表级意向锁与行级锁的兼容性如下所示:
插入意向锁(InsertIntentionlock)
插入意向锁是在插入一行记录操作之前设置的一种间隙锁,这个锁释放了一种插入方式的信号,即多个事务在相同的索引间隙插入时如果不是插入间隙中相同的位置就不需要互相等待。假设某列有索引值2,6,只要两个事务插入位置不同(如事务A插入3,事务B插入4),那么就可以同时插入。
锁模式兼容矩阵
横向是已持有锁,纵向是正在请求的锁:
--阅读死锁日志--
一个温馨小提示:xmen平台支持查看死锁主库的死锁日志,访问方式如下:
登录