0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

浅谈MySQL常见死锁场景

数据库和存储 来源:数据库和存储 2024-03-21 14:10 次阅读

在之前的文章MySQL 常见死锁场景 -- 并发Replace into导致死锁介绍了由于二级索引 unique key 导致的 deadlock, 其实主键也是 unique 的, 那么同样其实主键的 unique key check 一样会导致死锁.

主键 unique 的判断在

row_ins_clust_index_entry_low => row_ins_duplicate_error_in_clust

对于普通的INSERT操作, 当需要检查primary key unique时, 加 S record lock. 而对于Replace into 或者 INSERT ON DUPLICATE操作, 则加X record lock

这里check unique 的时候, 如果这里没有这个 record 存在, 加在下一个 record上, 如果已经有一个 delete_mark record, 那么就加在这个 delete marked record 上.

例子 1

create table t1 (a int primary key);

# 然后有三个不 session:

session1: begin; insert into t1(a) values (2);

session2: insert into t1(a) values (2);

session3: insert into t1(a) values (2);

session1: rollback;

rollback 之前:

这个时候 session2/session3 会wait 在这里2 等待s record lock, 因为session1 执行delete 时候会执行row_update_for_mysql => lock_clust_rec_modify_check_and_lock

这里会给要修改的record 加x record lock

insert 的时候其实也给record 加 x record lock, 只不过大部分时候先加implicit lock, 等真正有冲突的时候触发隐式锁的转换才会加上x lock

8d27b792-e72e-11ee-a297-92fbcf53809c.png

问题1: 这里为什么granted lock 里面 record 2 上面有x record lock 和 s record lock?

在session1 执行 rollback 以后, session2/session3 获得了s record lock, 在insert commit 时候发现死锁, rollback 其中一个事务, 另外一个提交, 死锁信息如下

8d3dfa66-e72e-11ee-a297-92fbcf53809c.png

这里看到 trx1 想要 x insert intention lock.

但是trx2 持有s next-key lock 和 trx1 x insert intention lock 冲突.

同时trx 也在等待 x insert intention lock, 这里从上面的持有Lock 可以看到 肯定在等待trx1 s next-key lock

问题: 等待的时候是 S gap lock, 但是死锁的时候发现是 S next-key lock. 什么时候进行的升级?

这里问题的原因是这个 table 里面只有record 2, 所以这里认真看, 死锁的时候是等待在 supremum 上的, 因为supremum 的特殊性, supremum 没有gap lock, 只有 next-key lock

0: len 8; hex 73757072656d756d: asc supremum; // 这个是等在supremum 记录

在 2 后面插入一个 3 以后, 就可以看到在record 3 上面是有s gap lock 并不是next-key lock, 如下图:

8d43417e-e72e-11ee-a297-92fbcf53809c.png

那么这个 gap lock 是哪来的?

这里gap lock 是在 record 3 上的. 这个record 3 的s lock 从哪里来? session2/3 等待在record 2 上的s record lock 又到哪里去了?

这几涉及到锁升级, 锁升级主要有两种场景

insert record, 被next-record 那边继承锁. 具体代码 lock_update_insert

delete record(注意这里不是delete mark, 必须是purge 的物理delete), 需要将该record 上面的lock, 赠给next record上, 具体代码 lock_update_delete

并且由于delete 的时候, 将该record 删除, 如果有等待在该record 上面的record lock, 也需要迁移到next-key 上, 比如这个例子wait 在record 2 上面的 s record lock

另外对于wait 在被删除的record 上的trx, 则通过 lock_rec_reset_and_release_wait(block, heap_no); 将这些trx 唤醒

具体看 InnoDB Trx lock

总结:

2 个trx trx2/trx3 都等待在primary key 上, 锁被另外一个 trx1 持有. trx1 回滚以后, trx2 和 trx3 同时持有了该 record 的 s lock, 通过锁升级又升级成下一个 record 的 GAP lock. 然后两个 trx 同时插入的时候都需要获得insert_intention lock(LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION); 就变成都想持有insert_intention lock, 被卡在对方持有 GAP S lock 上了.

例子 2

mysql> select * from t1;
+—-+
| a |
+—-+
| 2 |

| 3 |

+—-+

然后有三个不同 session:

session1: begin; delete from t1 where a = 2;

session2: insert into t1(a) values (2);

session3: insert into t1(a) values (2);

session1: commit;

commit之前

8d57c40a-e72e-11ee-a297-92fbcf53809c.png

这个时候session2/3 都在等待s record 2 lock, 等待时间是 innodb_lock_wait_timeout,

commit 之后

在session1 执行 commit 以后, session2/session3 获得到正在waiting的 s record lock, 在commit 的时候, 发现死锁, rollback 其中一个事务, 另外一个提交, 死锁信息如下

8d5c4426-e72e-11ee-a297-92fbcf53809c.png

trx1 等待x record lock, trx2 持有s record lock(这个是在session1 commit, session2/3 都获得了s record lock)

不过这样发现和上面例子不一样的地方, 这里的record 都lock 在record 2 上, 而不是record 3, 这是为什么?

本质原因是这里的delete 操作是 delete mark, 并没有从 btree 上物理删除该record, 因此还可以保留事务的lock 在record 2 上, 如果进行了物理删除操作, 那么这些record lock 都有迁移到next record 了

问题: 这里insert 操作为什么不是 insert intention lock?

比如如果是sk insert 操作就是 insert intention lock. 而这里是 s record lock?

8d76dea8-e72e-11ee-a297-92fbcf53809c.png

这里delete record 2 以后, 由于record 是 delete mark, 记录还在, 因此insert 的时候会将delete mark record改成要写入的这个record(这里不是可选择优化, 而是btree 唯一性, 必须这么做). 因此插入就变成 row_ins_clust_index_entry_by_modify

所以不是insert 操作, 因此就没有 insert intention lock.

而sk insert 的时候是不允许将delete mark record 复用的, 因为delete mark record 可能会被别的readview 读取到.

8d95d970-e72e-11ee-a297-92fbcf53809c.png

通过GDB + call srv_debug_loop() 可以让GDB 将进程停留在 session1 提交, 但是session2/3 还没有进入死锁之前, 这个时候查询performance_schema 可以看到session2/3 获得了record 10 s lock. 这个lock 怎么获得的呢?

这个和上述的例子一样, 这里因为等的比较久了, 所以发生了purge, 因为record 2 被物理删除了. 因此发生了锁升级, record 2 上面的record 会转给next-record, 这里next-record 是10,

总结:

和上一个例子基本类似.

2 个trx trx2/trx3 都等待在primary key 上的唯一性检查上, 锁被另外一个 trx1 持有. trx1 commit 以后, trx2 和 trx3 同时持有了该 record 的 s record lock, 然后由于 delete mark record 的存在, insert 操作变成 modify 操作, 因此就变成都想持有X record lock, 被卡在对方持有 S recordlock 上了.

审核编辑:黄飞

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 死锁
    +关注

    关注

    0

    文章

    25

    浏览量

    8046
  • MySQL
    +关注

    关注

    1

    文章

    775

    浏览量

    26003

原文标题:MySQL 常见死锁场景-- 并发插入相同主键场景

文章出处:【微信号:inf_storage,微信公众号:数据库和存储】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    浅谈原理图和PCB图的常见错误

    浅谈原理图和PCB图的常见错误
    发表于 08-12 13:04

    MySQL和MongoDB的对比

    中的文档不需要具有相同的一组字段,数据的非规范化是常见的。 MongoDB还设计了高可用性和可扩展性,并提供了即用型复制和自动分片功能。术语和概念 MySQL中的许多概念在MongoDB中具有相近
    发表于 08-28 14:51

    蓝牙低功耗常见的应用场景及架构

    浅谈蓝牙低功耗(BLE)的几种常见的应用场景及架构
    发表于 06-15 09:51

    死锁是什么?产生死锁的主要原因有哪些

    嵌入式系统设计师十二:进程管理③进程管理:死锁死锁概念:进程管理是操作系统的核心,但如果设计不当,就会出现死锁的问题。如果一个进程在等待一个不可能的事,则进程就死锁了。而如果一个或多个
    发表于 12-22 07:34

    DIN中的死锁避免和死锁恢复

    DIN中的死锁避免和死锁恢复 由于存在占用资源者申请另一个资源的情形,在DIN中由于拓扑结构本身存在环状路径,所以
    发表于 02-23 14:47 841次阅读
    DIN中的<b class='flag-5'>死锁</b>避免和<b class='flag-5'>死锁</b>恢复

    盘点MySQL常见问题及解答

    MySQL常见问题及答案汇总,MySQL是一种开放源代码的关系型数据库管理系统。数据库按照数据结构来组织、存储和管理数据的仓库。每个数据库都有一个或多个不同的 API 用于创建,访问,管理,搜索和复制所保存的数据。
    的头像 发表于 01-03 15:25 2248次阅读

    MySQL中的高级内容详解

    之前两篇文章带你了解了 MySQL 的基础语法和 MySQL 的进阶内容,那么这篇文章我们来了解一下 MySQL 中的高级内容。 其他文章: 138 张图带你 MySQL 入门 47
    的头像 发表于 03-11 16:55 1973次阅读
    <b class='flag-5'>MySQL</b>中的高级内容详解

    为什么分页场景mysql请求速度非常慢

    来源丨https://juejin.cn/post/6844903939247177741 从一个问题说起五年前在tx的时候,发现分页场景下,mysql请求速度非常慢。数据量只有10w的情况下
    的头像 发表于 10-08 14:46 1349次阅读
    为什么分页<b class='flag-5'>场景</b>下<b class='flag-5'>mysql</b>请求速度非常慢

    MySQL并发Replace into导致死锁场景简析

    在之前的文章 #issue 68021 MySQL unique check 问题中, 我们已经介绍了在 MySQL 里面, 由于唯一键的检查(unique check), 导致 MySQL 在 Read Commit 隔离级别
    的头像 发表于 06-13 10:56 629次阅读
    <b class='flag-5'>MySQL</b>并发Replace into导致<b class='flag-5'>死锁</b><b class='flag-5'>场景</b>简析

    通过GDB non-stop mode调试MySQL

    通过GDB non-stop mode 调试MySQL, 特别是用于复现死锁场景, 需要按照一定的并发顺序写入才可以构造出来, 通过GDB non-stop mode 可以非常方便进行构造
    的头像 发表于 09-25 10:34 303次阅读
    通过GDB non-stop mode调试<b class='flag-5'>MySQL</b>

    Linux内核死锁lockdep功能

    的编程思路,也不可能避免会发生死锁。在Linux内核中,常见死锁有如下两种: 递归死锁:如在中断延迟操作中使用了锁,和外面的锁构成了递归死锁
    的头像 发表于 09-27 15:13 363次阅读
    Linux内核<b class='flag-5'>死锁</b>lockdep功能

    死锁的产生因素

    一、死锁的概念 操作系统中的死锁是指: 如果在一个进程集合中的每个进程都在等待只能有该集合中的其它进程才能引起的事件,而无限期陷入僵持的局面称为死锁。 二、死锁的产生因素 1、系统拥有
    的头像 发表于 11-09 09:37 471次阅读
    <b class='flag-5'>死锁</b>的产生因素

    死锁的现象及原理

    原理 1.1 复现最简单的死锁 线程A占有锁1,线程B占有锁2;此时线程A想要获取锁2,但是锁2已经被线程B占有, 此时线程A会休眠等待线程B释放锁2后,再去获得锁2。可以看到下面的场景,线程B想要获取锁1,结果线程B也休眠去了。这就导致
    的头像 发表于 11-10 16:32 212次阅读
    <b class='flag-5'>死锁</b>的现象及原理

    死锁的现象以及原理

    前言 本文将从0到1写一个死锁检测组件。源码:deadlock_success.c 组件如何放入自己的项目里?把代码末两个Debug部分删除,在你的项目里添加下面两句代码即可使用死锁检测组件
    的头像 发表于 11-13 16:30 273次阅读
    <b class='flag-5'>死锁</b>的现象以及原理

    java死锁产生的条件

    Java死锁是指多个线程因为互相等待对方释放资源而无法继续执行的情况。当线程处于死锁状态时,程序会无限期地等待资源,无法继续执行下去,从而导致整个系统的停滞。要理解并避免Java死锁的产生,首先需要
    的头像 发表于 12-04 13:42 185次阅读