由于加锁顺序不一致造成的死锁在RC(READ-COMMIT)隔离级别下较为常见,本文分享一个RR(REPEATABLE-READ)隔离级别下由于GAP锁导致的死锁案例。
得知有死锁发生时,首先查看数据库隔离级别为默认的RR(REPEATABLE-READ)模式:
然后再查看innodb存储引擎日志可以看到最近一次死锁的相关信息:
从日志中我们可以看出死锁发生在表T_ORDERBILL_TASK上而且是两条相同类型的针对表T_ORDERBILL_TASK插入的insert语句!检查T_ORDERBILL_TASK表结构如下:
可见ORG_CODE上有二级索引。
(1) TRANSACTION也就是第一个事务的信息中:
l WAITING FOR THIS LOCK TO BE GRANTED,表示的是这个事务在等待的锁信息;
l index ORG_CODE of table `T_BSE_QUERY_SCHEME`.`T_ORDERBILL_TASK`表示等的是表T_ORDERBILL_TASK上ORG_CODE索引上面的锁;
l lock_mode X locks gap before rec insert intention waiting Record lock 表示这个语句要加一个插入意向锁但是还在等待状态,Record lock说明这是一个记录锁。
(2) TRANSACTION 也就是第二个事务中:
l (2) HOLDS THE LOCK(S)中lock_mode X locks gap before rec 表示了持有GAP锁
l (2) WAITING FOR THIS LOCK TO BE GRANTED中lock_mode X locks gap before rec insert intention waiting同(1)TRANSACTION等待加插入意向锁
结合业务开发提供的该模块儿的业务逻辑和发生死锁时间上下文业务日志可以整理出事务1和事务2的执行顺序为:
或者
可以看出在插入一条数据前先执行当前读锁住该条数据,若不存在则插入该条数据,否则执行update。
由于索引是顺序存储的,查出110026介于110019和110036之间:
由此可知:
当session 1执行select ... for update当前读,由于ORG_CODE = 110026的数据不存在,因此会加上gap锁(110019,110036)。
Session 2执行select ... for update当前读,同样也会加上gap锁(110019,110036),由于gap锁不互斥,所以该语句也可成功执行。
但是到第三步时无论是session 1先插入还是session 2先插入都会等待对方的gap锁,第四步时后插入的也会被对方的gap锁阻塞。两个session进入互相等待状态形成死锁,最后死锁检测将session2回滚。
其实GAP锁,就是RR隔离级别相对于RC隔离级别不会出现幻读的关键。在RR隔离级别下,条件列上为非唯一索引时,当前读通过条件列未定位到满足条件的记录时会加GAP锁,保证后续的insert不能插入相同值的数据,以防止出现幻读。需要注意的是GAP锁并不互斥但是和插入意向锁互斥。相同语句的情况下RR模式由于会加GAP锁可能锁住更多的数据,虽然防止了幻读,但是会影响一定的并发。在配置RC和RR隔离级别的时候一定要根据业务场景进行选择。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/130127.html
摘要:问题描述近期项目需要从虚拟机环境迁移到容器环境,其中有一个项目在迁移到容器环境之后的两天之内出现了次死锁的问题,部分关键日志如下日志还是挺明显的,线程获得了锁,等待获取而正好相反,从而导致死锁问题分析以上的错误 问题描述 近期项目需要从虚拟机环境迁移到容器环境,其中有一个项目在迁移到容器环境之后的两天之内出现了2次死锁(deadlock)的问题,部分关键日志如下: Found one ...
摘要:的情况下,必然就会死锁,对吧,接下来怎么用验证呢切到号线程查看线程栈及栈对象。死锁原因分析死锁原因分析要想追究死锁的原因,只能仔细推敲线程栈线程栈对象。在几个痉挛过程中进入了另外一个线程池的方法中,希望能得到该池中的锁对象。一:背景1. 讲故事这个月初,星球里的一位朋友找到我,说他的程序出现了死锁,怀疑是自己的某些写法导致mongodb出现了如此尴尬的情况,截图如下:说实话,看过这么多dum...
摘要:此时我想到了福尔摩斯说过的一句话当你排除掉各种不可能出现的情况之后,剩下的情况无论多么难以置信,都是真相。福尔摩斯冷静下来想一想,这个线程,有可能静悄悄地退出了吗,没留下半点异常日志从理论上来说,不可能。配置建议最后,附上一份配置建议。 1、事发 我们有个视频处理程序,基于 SpringBoot,会启动几个线程来跑。要退出程序时,会发送一个信号给程序,每个线程收到信号后会平滑退出,等全...
阅读 1247·2023-01-11 13:20
阅读 1555·2023-01-11 13:20
阅读 1009·2023-01-11 13:20
阅读 1676·2023-01-11 13:20
阅读 3968·2023-01-11 13:20
阅读 2510·2023-01-11 13:20
阅读 1306·2023-01-11 13:20
阅读 3474·2023-01-11 13:20