MySQL死锁解析
我是本际云服务器推荐网的小编小本本。在工作中,每个MySQLDBA和开发都可能会遇到死锁问题。死锁是并发系统中常见的问题,特别是在数据库系统的并发读写请求场景中。当两个及以上的事务出现互相等待对方释放已经持有的锁的情况,就会出现死锁。本文将分享一个RR(REPEATABLE-READ)隔离级别下由于GAP锁导致的死锁案例。

案例
当得知有死锁发生时,首先查看数据库隔离级别为默认的RR(REPEATABLE-READ)模式。然后再查看innodb存储引擎日志,可以看到最近一次死锁的相关信息!从日志中可以看出死锁发生在表T_ORDERBILL_TASK上,而且是两条相同类型的针对表T_ORDERBILL_TASK插入的insert语句!
检查T_ORDERBILL_TASK表结构,可见ORG_CODE上有二级索引。TRANSACTION也就是第一个事务的信息中:WAITINGFORTHISLOCKTOBEGRANTED表示的是这个事务在等待的锁信息;indexORG_CODEoftable`T_BSE_QUERY_SCHEME`.`T_ORDERBILL_TASK表示等的是表T_ORDERBILL_TASK上ORG_CODE索引上面的锁。lock_modeXlocksgapbeforerecinsertintentionwaitingRecordlock表示这个语句要加一个插入意向锁但是还在等待状态,Recordlock说明这是一个记录锁。TRANSACTION也就是第二个事务中:(2)HOLDSTHELOCK(S)中lock_modeXlocksgapbeforerec表示了持有GAP锁,(2)WAITINGFORTHISLOCKTOBEGRANTED中lock_modeXlocksgapbeforerecinsertintentionwaiting同(1)TRANSACTION等待加插入意向锁。结合业务开发提供的该模块的业务逻辑和发生死锁时间上下文业务日志,可以整理出事务1和事务2的执行顺序。
或者可以看出,在插入一条数据前先执行当前读锁住该条数据,若不存在则插入该条数据,否则执行update。由于索引是顺序存储的,查出110026介于110019和110036之间。
由此可知:当session1执行select…forupdate当前读,由于ORG_CODE=110026的数据不存在,因此会加上gap锁(110019,110036)。Session2执行select…forupdate当前读,同样也会加上gap锁(110019,110036),由于gap锁不互斥,所以该语句也可成功执行。但是到第三步时无论是session1先插入还是session2先插入都会等待对方的gap锁,第四步时后插入的也会被对方的gap锁阻塞。两个session进入互相等待状态形成死锁,最后死锁检测将session2回滚。
结论
GAP锁是RR隔离级别相对于RC隔离级别不会出现幻读的关键。在RR隔离级别下,条件列上为非唯一索引时,当前读通过条件列未定位到满足条件的记录时会加GAP锁,保证后续的insert不能插入相同值的数据,以防止出现幻读。需要注意的是GAP锁并不互斥但是和插入意向锁互斥。相同语句的情况下RR模式由于会加GAP锁可能锁住更多的数据,虽然防止了幻读,但是会影响一定的并发。在配置RC和RR隔离级别的时候一定要根据业务场景进行选择。
原创文章,作者:小编小本本,如若转载,请注明出处:https://www.benjiyun.com/yunzhujiyunwei/vps-yunwei/5840.html
