美文网首页
Mysql死锁的形成与解决

Mysql死锁的形成与解决

作者: 皮儿吃屁 | 来源:发表于2020-09-17 17:37 被阅读0次

    死锁的形成

    死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

    死锁的案例

    案例一
    事务A 事务B
    UPDATE students SET score = 100 WHERE id = 20
    UPDATE students SET score = 100 WHERE id = 30
    UPDATE students SET score = 100 WHERE id = 30
    UPDATE students SET score = 100 WHERE id = 20

    首先,事务 A 获取 id = 20 的锁(lock_mode X locks rec but not gap),事务 B 获取 id = 30 的锁;然后,事务 A 试图获取 id = 30 的锁,而该锁已经被事务 B 持有,所以事务 A 等待事务 B 释放该锁,然后事务 B 又试图获取 id = 20 的锁,这个锁被事务 A 占有,于是两个事务之间相互等待,导致死锁。

    案例二
    事务A 事务B
    UPDATE students SET score = 100 WHERE id = 25
    UPDATE students SET score = 100 WHERE id = 26
    INSERT INTO students (id,name,no) VALUE (25,'lilei',1)
    INSERT INTO students (id,name,no) VALUE (26,'zhangsan',2)

    首先事务 A 和事务 B 执行了两条 UPDATE 语句,但是由于 id = 25 和 id = 26 记录都不存在,事务 A 和 事务 B 并没有更新任何记录,但是由于数据库隔离级别为 RR,所以会在 (20, 30) 之间加上间隙锁(lock_mode X locks gap before rec),间隙锁和间隙锁并不冲突。之后事务 A 和事务 B 分别执行 INSERT 语句要插入记录 id = 25 和 id = 26,需要在 (20, 30) 之间加插入意向锁(lock_mode X locks gap before rec insert intention),插入意向锁和间隙锁冲突,所以两个事务互相等待,最后形成死锁。

    案例三
    事务A 事务B
    UPDATE students SET score = 100 WHERE id > 20
    UPDATE students SET score = 100 WHERE id < 30

    别看这个案例里每个事务都只有一条 SQL 语句,但是却实实在在可能会导致死锁问题,其实说起来,这个死锁和案例一并没有什么区别,只不过理解起来要更深入一点。要知道在范围查询时,加锁是一条记录一条记录挨个加锁的,所以虽然只有一条 SQL 语句,如果两条 SQL 语句的加锁顺序不一样,也会导致死锁。

    如何避免死锁

    对于 MySQL 的 InnoDb 存储引擎来说,死锁问题是避免不了的,没有哪种解决方案可以说完全解决死锁问题,但是我们可以通过一些可控的手段,降低出现死锁的概率。

    1. 如上面的案例一和案例三所示,对索引加锁顺序的不一致很可能会导致死锁,所以如果可以,尽量以相同的顺序来访问索引记录和表。在程序以批量方式处理数据的时候,如果事先对数据排序,保证每个线程按固定的顺序来处理记录,也可以大大降低出现死锁的可能;
    2. 如上面的案例二所示,Gap 锁往往是程序中导致死锁的真凶,由于默认情况下 MySQL 的隔离级别是 RR,所以如果能确定幻读和不可重复读对应用的影响不大,可以考虑将隔离级别改成 RC,可以避免 Gap 锁导致的死锁;
    3. 为表添加合理的索引,如果不走索引将会为表的每一行记录加锁,死锁的概率就会大大增大;
    4. 我们知道 MyISAM 只支持表锁,它采用一次封锁技术来保证事务之间不会发生死锁,所以,我们也可以使用同样的思想,在事务中一次锁定所需要的所有资源,减少死锁概率;
    5. 避免大事务,尽量将大事务拆成多个小事务来处理;因为大事务占用资源多,耗时长,与其他事务冲突的概率也会变高;
    6. 避免在同一时间点运行多个对同一表进行读写的脚本,特别注意加锁且操作数据量比较大的语句;我们经常会有一些定时脚本,避免它们在同一时间点运行;
    7. 设置锁等待超时参数:innodb_lock_wait_timeout,这个参数并不是只用来解决死锁问题,在并发访问比较高的情况下,如果大量事务因无法立即获得所需的锁而挂起,会占用大量计算机资源,造成严重性能问题,甚至拖跨数据库。我们通过设置合适的锁等待超时阈值,可以避免这种情况发生。

    参考链接:https://www.aneasystone.com/archives/2018/04/solving-dead-locks-four.html

    相关文章

      网友评论

          本文标题:Mysql死锁的形成与解决

          本文链接:https://www.haomeiwen.com/subject/acjlyktx.html