概述
每个MySQL DBA和开发可能都会在工作过程中遇到死锁问题,死锁是并发系统中比较常见的问题,同样也会出现在数据库系统的并发读写请求场景中。当两个及以上的事务,双方都在等待对方释放已经持有的锁或因为加锁顺序不一致造成循环等待锁资源,就会出现"死锁"。
由于加锁顺序不一致造成的死锁在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也就是第一个事务的信息中:
WAITING FOR THIS LOCK TO BE GRANTED,表示的是这个事务在等待的锁信息;
index ORG_CODE of table `T_BSE_QUERY_SCHEME`.`T_ORDERBILL_TASK`表示等的是表T_ORDERBILL_TASK上ORG_CODE索引上面的锁;
lock_mode X locks gap before rec insert intention waiting Record lock 表示这个语句要加一个插入意向锁但是还在等待状态,Record lock说明这是一个记录锁。
2. TRANSACTION 也就是第二个事务中:
(2) HOLDS THE LOCK(S)中lock_mode X locks gap before rec 表示了持有GAP锁
(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隔离级别的时候一定要根据业务场景进行选择。
更多精彩干货分享
点击下方名片关注
IT那活儿
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/129763.html
摘要:问题描述近期项目需要从虚拟机环境迁移到容器环境,其中有一个项目在迁移到容器环境之后的两天之内出现了次死锁的问题,部分关键日志如下日志还是挺明显的,线程获得了锁,等待获取而正好相反,从而导致死锁问题分析以上的错误 问题描述 近期项目需要从虚拟机环境迁移到容器环境,其中有一个项目在迁移到容器环境之后的两天之内出现了2次死锁(deadlock)的问题,部分关键日志如下: Found one ...
摘要:的情况下,必然就会死锁,对吧,接下来怎么用验证呢切到号线程查看线程栈及栈对象。死锁原因分析死锁原因分析要想追究死锁的原因,只能仔细推敲线程栈线程栈对象。在几个痉挛过程中进入了另外一个线程池的方法中,希望能得到该池中的锁对象。一:背景1. 讲故事这个月初,星球里的一位朋友找到我,说他的程序出现了死锁,怀疑是自己的某些写法导致mongodb出现了如此尴尬的情况,截图如下:说实话,看过这么多dum...
摘要:此时我想到了福尔摩斯说过的一句话当你排除掉各种不可能出现的情况之后,剩下的情况无论多么难以置信,都是真相。福尔摩斯冷静下来想一想,这个线程,有可能静悄悄地退出了吗,没留下半点异常日志从理论上来说,不可能。配置建议最后,附上一份配置建议。 1、事发 我们有个视频处理程序,基于 SpringBoot,会启动几个线程来跑。要退出程序时,会发送一个信号给程序,每个线程收到信号后会平滑退出,等全...
摘要:记一次优惠券最优使用算法先说一下业务背景。公司做的一个投资的,投资金额可以用优惠券抵扣。但是无法获取具体使用了哪张优惠券简单就是很难获得优惠券的穷举法数据太多,不可控。但是这种面额的优惠券出现几率几乎没有请教期待有大神给出更好的算法 记一次优惠券最优使用算法 先说一下业务背景。公司做的一个投资的APP,投资金额可以用优惠券抵扣。红包面额(100,50,30,10) 优惠券使用规则: ...
阅读 1247·2023-01-11 13:20
阅读 1555·2023-01-11 13:20
阅读 1008·2023-01-11 13:20
阅读 1676·2023-01-11 13:20
阅读 3968·2023-01-11 13:20
阅读 2510·2023-01-11 13:20
阅读 1305·2023-01-11 13:20
阅读 3474·2023-01-11 13:20