关于mysql:死锁问题排查过程间隙锁的复现以及解决

39次阅读

共计 1242 个字符,预计需要花费 4 分钟才能阅读完成。

问题背景

咱们在开启多线程对数据库进行操作的时候,先批量对数据进行删除,而后再新增,原本想着是思考到不走更新,性能会晋升,然而执行的时候发现报错,执行的 sql 期待超时,阻塞了过程,dbcp 连接池被打满,数据库表拜访不可用。针对这个问题,咱们进行了深刻的开掘,逐步解开了问题的假相。

看下具体的业务实现细节

  • 表定义
  • 当初导⼊⼀批数据 A 的汇合,A 的定义如下所示:

接下来复现问题操作

  • 依据 t1 的值查问表 a 中有没有对应的记录
  • 如果有值,则更新 t2 的值
  • 如果没查到后果,则执行 insert 插入操作

这里批量操作咱们采纳了多线程的形式来执行

问题复现

  • step1 – ⾸先插⼊测试数据
  • step2 – 咱们开启两个窗⼝去模仿死锁。
    Session1:

    Session2:

    此时,Session 1 和 Session 2 都会对区间 (20, ⽆穷⼤) 加锁, 因为间隙锁只是⽤来防⽌其余事务在区间中插⼊数据。
  • step3 – Session1 持续插⼊操作:

此时 Session1 阻塞(因为 Session2 持有间隙锁)。

  • step4- 紧接着 Session2 持续插⼊操作:

此时 Session2 死锁,因为 Session1 持有间隙锁。⽽咱们的代码⾥⾯,因为波及到多线程在事务⾥进⾏先删除后插⼊的操作,就会发⽣死锁。

不走更新操作,先删除,后插入,保障只有 2 次数据库操作。

问题起因

查问相干材料得悉,引起死锁的起因是 MYSQL 的间隙锁。
间隙锁

间隙锁(Gap Lock)是 Innodb 在可反复读提交下为了解决幻读问题时引⼊的锁机制,幻读的问题存在是因为新增或者更新操作,这时如果进⾏范畴查问的时候(加锁查问),会呈现不⼀致的问题,这时使⽤不同的⾏锁曾经没有方法满⾜要求,须要对⼀定范畴内的数据进⾏加锁,间隙锁就是解决这类问题的。在可反复读隔离级别下,数据库是通过⾏锁和间隙锁独特组成的(next-key lock)来实现的。
⾏锁和间隙锁的定义如下所示:

  • record lock:⾏锁,也就是仅仅锁着独自的⼀⾏。
  • gap lock:间隙锁,仅仅锁住⼀个区间(留神这⾥的区间都是开区间,也就是不包含边界值)。
  • next-key lock:record lock+gap lock,所以 next-key lock 也就半开半闭区间,且是下界开,上界闭。
    加锁规定个性

加锁规定有⼀些个性,其中咱们须要关注的有:

  • 加锁的根本单位是(next-key lock), 他是前开后闭准则
  • 查找过程中拜访的对象会减少锁
  • 间隙锁仅阻⽌其余事务插⼊间隙。在删除数据的时候,会去加间隙锁,然而多个事务是能够同时对⼀个间隙去加锁的,⽽如果须要对该间隙进⾏插⼊,则须要期待锁开释。

解决形式

1、将事务隔离级别将为 read commit.

间隙锁只存在于可反复读的隔离级别下,因为要防⽌幻读。这个⽅法不事实,不可能为了这个问题 把整个线上数据库隔离级别给改掉。
2、防止先删除后插⼊的操作.

批改代码,防止先删除后插⼊的操作。就义性能,在业务中,先依据唯⼀索引查出存在的记录,而后对存在的记录进⾏依据主键 Id 在循环中更新,对于不存在的记录进⾏批量插⼊。

正文完
 0