当前位置: 首页 > MySQL数据库

INSERT语句引发的死锁实例分析

时间:2026-01-28 12:53:32

两条一样的INSERT语句竟然引发了死锁,这究竟是人性的扭曲,还是道德的沦丧,让我们不禁感叹一句:卧槽!这也能死锁,然后眼中含着悲催的泪水无奈的改起了业务代码。

好的,在深入分析为啥两条一样的INSERT语句也会产生死锁之前,我们先介绍一些基础知识。

准备一下环境

为了故事的顺利发展,我们新建一个用了无数次的hero表:

CREATE TABLE hero (    number INT AUTO_INCREMENT,    name VARCHAR(100),    country varchar(100),    PRIMARY KEY (number),    UNIQUE KEY uk_name (name)) Engine=InnoDB CHARSET=utf8;

然后向这个表里插入几条记录:

INSERT INTO hero VALUES    (1, 'l刘备', '蜀'),    (3, 'z诸葛亮', '蜀'),    (8, 'c曹操', '魏'),    (15, 'x荀彧', '魏'),    (20, 's孙权', '吴');

现在hero表就有了两个索引(一个唯一二级索引,一个聚簇索引),示意图如下:

INSERT语句如何加锁

读过《MySQL是怎样运行的:从根儿上理解MySQL》的小伙伴肯定知道,INSERT语句在正常执行时是不会生成锁结构的,它是靠聚簇索引记录自带的trx_id隐藏列来作为隐式锁来保护记录的。

但是在一些特殊场景下,INSERT语句还是会生成锁结构的,我们列举一下:

1. 待插入记录的下一条记录上已经被其他事务加了gap锁时

每插入一条新记录,都需要看一下待插入记录的下一条记录上是否已经被加了gap锁,如果已加gap锁,那INSERT语句应该被阻塞,并生成一个插入意向锁。

比方说对于hero表来说,事务T1运行在REPEATABLE READ(后续简称为RR,后续也会把READ COMMITTED简称为RC)隔离级别中,执行了下边的语句:

# 事务T1mysql> BEGIN;Query OK, 0 rows affected (0.00 sec)mysql> SELECT * FROM hero WHERE number < 8 FOR UPDATE;+--------+------------+---------+| number | name       | country |+--------+------------+---------+|      1 | l刘备      | 蜀      ||      3 | z诸葛亮    | 蜀      |+--------+------------+---------+2 rows in set (0.02 sec)

这条语句会对主键值为1、3、8的这3条记录都添加X型next-key锁,不信的话我们使用SHOW ENGINE INNODB STATUS语句看一下加锁情况,图中箭头指向的记录就是number值为8的记录:

此时事务T2想插入一条主键值为4的聚簇索引记录,那么T2在插入记录前,首先要定位一下主键值为4的聚簇索引记录在页面中的位置,发现主键值为4的下一条记录的主键值是8,而主键值是8的聚簇索引记录已经被添加了gap锁(next-key锁包含了正经记录锁和gap锁),那么事务1就需要进入阻塞状态,并生成一个类型为插入意向锁的锁结构。

我们在事务T2中执行一下INSERT语句验证一下:

mysql> BEGIN;Query OK, 0 rows affected (0.00 sec)mysql> INSERT INTO hero VALUES(4, 'g关羽', '蜀');

此时T2进入阻塞状态,我们再使用SHOW ENGINE INNODB STATUS看一下加锁情况:

可见T2对主键值为8的聚簇索引记录加了一个插入意向锁(就是箭头处指向的lock_mode X locks gap before rec insert intention),并且处在waiting状态。

好了,验证过之后,我们再来看看代码里是如何实现的:

lock_rec_insert_check_and_lock函数用于看一下别的事务是否阻止本次INSERT插入,如果是,那么本事务就给被别的事务添加了gap锁的记录生成一个插入意向锁,具体过程如下:

2. 遇到重复键时

如果在插入新记录时,发现页面中已有的记录的主键或者唯一二级索引列与待插入记录的主键或者唯一二级索引列值相同(不过可以有多条记录的唯一二级索引列的值同时为NULL,这里不考虑这种情况了),此时插入新记录的事务会获取页面中已存在的键值相同的记录的锁。

如果是主键值重复,那么:

  • 英特尔与 Vertiv 合作开发液冷 AI 处理器
  • 英特尔第五代 Xeon CPU 来了:详细信息和行业反应
  • 由于云计算放缓引发扩张担忧,甲骨文股价暴跌
  • Web开发状况报告详细介绍可组合架构的优点
  • 如何使用 PowerShell 的 Get-Date Cmdlet 创建时间戳
  • 美光在数据中心需求增长后给出了强有力的预测
  • 2027服务器市场价值将接近1960亿美元
  • 生成式人工智能的下一步是什么?
  • 分享在外部存储上安装Ubuntu的5种方法技巧
  • 全球数据中心发展的关键考虑因素
  • 英特尔与 Vertiv 合作开发液冷 AI 处理器

    英特尔第五代 Xeon CPU 来了:详细信息和行业反应

    由于云计算放缓引发扩张担忧,甲骨文股价暴跌

    Web开发状况报告详细介绍可组合架构的优点

    如何使用 PowerShell 的 Get-Date Cmdlet 创建时间戳

    美光在数据中心需求增长后给出了强有力的预测

    2027服务器市场价值将接近1960亿美元

    生成式人工智能的下一步是什么?

    分享在外部存储上安装Ubuntu的5种方法技巧

    全球数据中心发展的关键考虑因素