看来,MySQL next-key lock 的 bug 并没有被修复

聊聊MySQL的加锁规则《死磕MySQL系列 十五》 本期来聊聊MySQL的加锁规则,知道这些规则后可以判断SQL语句的加锁范围,同时也可以写出更好的SQL语句,防止幻读问题的产生,在能力范围内最大程度的提升 阅读详情

前言

  1. 加锁时,会先给表添加意向锁,IX 或 IS;
  2. 加锁是如果是多个范围,是分开加了多个锁,每个范围都有锁;(这个可以实践下 id < 20 的情况)
  3. 主键等值查询,数据存在时,会对该主键索引的值加行锁 X,REC_NOT_GAP;
  4. 主键等值查询,数据不存在时,会对查询条件主键值所在的间隙添加间隙锁 X,GAP;
  5. 主键等值查询,范围查询时情况则比较复杂:
    1. 8.0.17 版本是前开后闭,而 8.0.18 版本及以后,修改为了前开后开区间;
    2. 临界 <= 查询时,8.0.17 会锁住下一个 next-key 的前开后闭区间,而 8.0.18 及以后版本,修复了这个 bug。

这篇文章会对非主键唯一索引进行操作实践。

1

数据库表数据

CREATE TABLE `t` (
  `id` int NOT NULL COMMENT '主键',
  `a` int DEFAULT NULL COMMENT '唯一索引',
  `c` int DEFAULT NULL COMMENT '普通索引',
  `d` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_a` (`a`),
   KEY `idx_c` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

数据库数据如下:

数据库的字段 a 是唯一索引。

2

非主键唯一索引

非主键唯一索引等值查询 —— 数据存在

mysql> begin; select * from t where a = 110 for update;

分析一下这条 SQL:

  1. select * 查询条件肯定会回表;
  2. 唯一索引,所以定位到数据后不需要继续查询;
  3. 猜测是锁住唯一索引及对应的主键索引。

查看 data_locks

  1. 表锁 IX;
  2. 索引 uniq_a 上面加了 X,REC_NOT_GAP 行锁,其中 110, 10 表示是 a = 110 这行数据,后面的 10 是这行数据对应的主键;
  3. 主键 id = 10 上添加了 X,REC_NOT_GAP 行锁。

一切和分析的一样。

如果把 for update 换成 for share,其实也是相同,在主键和唯一索引上都加了锁。

这里执行的 SQL 都是 select *,如果替换为 select id 呢?

mysql> begin; select id from t where a = 110 for update;

分析一下这条 SQL:

  1. select id 查询,满足索引覆盖,不会回表;
  2. 唯一索引,所以定位到数据后不需要继续查询;
  3. 那这里是锁主键索引还是两个都锁?

所以看出并无什么区别。

把 for update 换成 for share,这时候区别来了:

只有两条锁记录:表意向锁和 uniq_a 索引的 S,REC_NOT_GAP 锁。

很明显,for share 覆盖索引时,只是对自己的索引加锁。

update t set c = 2101 where id = 10;

这时候使用主键更新 c 是否能更新?那下面两个 SQL 呢?

update t set a = 1101 where id = 10;

update t set c = 2101 where a = 110;

执行结果很显然,第一个可以执行,而后两个是会阻塞的。

所以,非主键唯一索引等值查询,数据存在,for update 是会在主键加锁的,而 for share 只有在走覆盖索引的情况下,会仅在自己索引上加锁。

非主键唯一索引等值查询 —— 数据不存在

mysql> begin; select * from t where a = 111 for update;

分析这一条 SQL:

  1. 首先加了 for update,肯定会在 索引 uniq_a 和 主键索引上都加上锁;
  2. 字段 a 具有唯一性,但是数据 a = 111 不存在,会一直查,查到 115 区间;
  3. 所以会加索引 uniq_a 和 主键索引的间隙锁。(并不对 )

事实证明,分析结果不正确。

并且我执行 update t set c = 2101 where id = 15; 也过了。

所以是不是可以理解为,非主键索引等值查询,数据不存在,相当于一个范围查询,仅仅会在非主键索引上加锁,加的还是间隙锁,前开后开区间;

如果此时走索引覆盖呢?其实结果也是相同的。

非主键唯一索引范围查询

mysql> begin; select * from t where a >= 110 and a < 115 for update;

分析 SQL

  1. a >= 110 and a < 115,非主键唯一索引 [110,115),肯定是要加锁的;
  2. 对应的主键索引 10 应该也会加锁!

事实证明,又一次是错误的!

分析 data_locks:

  1. 怎么会对非主键唯一索引的 110 加了锁?LOCK_MODE 还是 X,如果加了行锁我还能理解。
  2. 怎么会对非主键唯一索引的 115 加了锁?

很明显 110 和 115 之前的间隙以及它们自身的记录都被锁住了。

经过一番分析,难道是因为前开后闭。

脑袋炸裂呀,完全和主键索引的 next-key lock 加锁范围不同,人家 sql 是什么就说什么。

有小伙伴知道原因可以告诉我。

如果我把 sql 改成下面的这种呢?

mysql> begin; select *  from t where a > 110 and a < 114 for update;

诶???

奇了怪了!

我唯一能想到的原因就是前开后闭了。因为 a >= 10 中的等于是属于上一个区间的,所以需要锁住上一个区间。

我只能说懵逼三连了!!!

其实还是有结论的:

在非主键唯一索引范围查询时,会对相应的范围加前开后闭区间,并且如果存在数据,会对对应的主键加行锁。

这时候如果走覆盖索引呢??

mysql> begin; select id from t where a >= 110 and a < 115 for update;

按照刚才的思路,前开后闭:

  1. 锁住主键索引 110 的区间,115 的区间
  2. 锁住主键 10 的行锁

事实又错了!

还锁住了主键 15 的行锁。

把等号去掉 15 是锁住的。

感觉脑袋完全不够用啊。重点是我没有理解怎么主键还是前开后开,这里就前开后闭了?

难道我在这里试试那个 bug?

啪啪打脸啊!

之前还说这个 bug 在 8.0.18 被修复了,并优化成了前开后开区间,这直接打脸,明摆着没有修复。

我只是操作 a > 100 and a <= 115 for update; 竟然把 120 给我锁住了,不就是 next-key 的 bug。

尝试一下 sql

很明显~ 这个 bug 在非主键唯一索引上,并没有修复!!!

3

总结

在非主键唯一索引情况下:

  1. 非主键唯一索引等值查询,数据存在,for update 是会在主键加锁的,而 for share 只有在走覆盖索引的情况下,会仅在自己索引上加锁;
  2. 非主键索引等值查询,数据不存在,无论是否索引覆盖,相当于一个范围查询,仅仅会在非主键索引上加锁,加的还是间隙锁,前开后开区间;
  3. 在非主键唯一索引范围查询时,不是覆盖索引的时候,会对相应的范围加前开后闭区间,并且如果存在数据,会对对应的主键加行锁;
  4. 在非主键唯一索引范围查询时,如果是覆盖索引时,会对所有的后闭区间对应的主键,加行锁。

实践完本文的所有操作,个人处于有些懵逼的状态。我使用的版本是 8.0.25

  1. 主键不是前开后闭,而非主键唯一索引看样子又很遵循前开后闭原则;
  2. next key 的 bug 在非主键唯一索引上,并没有被修复!

仔细一想,似乎又可以理解。

因为主键上的 next-key 的 bug 被修复了,同时优化了前开后闭区间为前开后开区间,而非主键唯一索引上这个 bug 没有被修复,所以没有优化。

嗯~ 大概就是这样吧!

21 | 为什么我只改一行的语句,锁这么多? MySQL45讲 实践篇 21 | 为什么我只改一行的语句,锁这么多? 间隙锁在可重复读隔离级别下才有效。 next-key lock 加锁规则 两个“原则”、两个“优化”和一个“bug” 原则 1:加锁的基本单位是 next-key lock。 原则 2:查找过程中访问到的对象才会加锁。 优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。 优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。 bug:唯 阅读详情

相关推荐

MySQL Next-Key Lock Bug: 仍未修复的隐患

具体来说,Next-Key Lock bug出现在以下情况下:当一个事务要插入一个新的记录,并且该记录正好位于一个已存在的索引范围内时,MySQL会为该事务创建一个Next-Key Lock。具体来说,Next-Key Lock bug出现在以下情况下:当一个事务要插入一个新的记录,并且该记录正好位于一个已存在的索引范围内时,MySQL会为该事务创建一个Next-Key Lock。接着,事务A在索引范围内尝试插入一条id为2的记录,但由于事务B的锁定,事务A的插入操作被阻塞。

CyberGenius的博客 143

MySQL中的next-key lock

MySQL中的next-key locknext-key lock是啥有啥用幻读如何产生 再次推荐一下丁奇的《MySQL实战45讲》,不同阶段阅读该专栏,总能收获不同的感悟。阅读本文,即默认已经知道“当前读”和“快照读”的含义了。 next-key lock是啥 间隙锁(Gap锁)和行锁合称 next-key lock。以该表t为例子: 在MySQL采用innodb,可重复读模式的情况下,执行如下语句: select * from t for update 此时,for update情况下,sele

legendaryhaha的博客 4543

CVPR 2027 投稿规划:基于近3年录用率与截稿周期的5步时间管理法

本文为计算机视觉研究者提供CVPR 2027投稿的完整规划指南,基于近三年录用率与截稿周期数据,提出五步时间管理法。涵盖选题策略、实验设计、论文写作、同行评审到最终提交的全流程优化,特别针对CVPR日益激烈的竞争环境(2024年投稿量达11532篇,录取率仅23.6%)提供实用解决方案,帮助研究者提升投稿成功率。

weixin_34079177的博客 1180

MySQL记录锁、间隙锁、临键锁(Next-Key Locks)详解

唯一索引等值查询:1.当查询的记录是存在的,next-key lock 会退化成「记录锁」。2.当查询的记录是不存在的,next-key lock 会退化成「间隙锁」。非唯一索引等值查询:1.当查询的记录存在时,除了会加 next-key lock 外,还额外加间隙锁,也就是会加两把锁。2.当查询的记录不存在时,只会加 next-key lock,然后会退化为间隙锁,也就是只会加一把锁。

yzx3105的博客 7934

SARscape的DInSAR全流程-以西安市隋大兴唐长安城遗址为例

通过DInSAR对西安市隋大兴唐长安城遗址开展的年度周期性监测,发现成因是地质内部构造断裂带活动所导致的地面产生物理空间上的形变,而地面的沉降与抬升是直接影响城市发展与文化遗产保护最为常见的缓变型不可逆地质灾害。西安市地处汾渭断陷盆地中部,受临潼-长安断裂带影响,导致区域内构造活动较为强烈。由于早年西安市地表水资源匮乏,从上世纪50年代末开始大量开采地下水资源以满足城市人口不断扩张的需求,从而导致地下承压含水层水位急速下降,形成多个局部沉降中心(八里村、西工大、电子城等)。

2301_77687129的博客 5819

模拟IC设计避坑:手把手教你用Cadence仿真恒定跨导轨到轨运放(附完整工艺库文件)

本文详细解析了使用Cadence仿真恒定跨导轨到轨运放的全流程,涵盖电路原理、工艺库配置、尺寸设计及仿真分析。通过1:3电流镜补偿技术实现跨导稳定,提供CSMC 0.25um工艺下的实战案例,帮助工程师规避设计陷阱,提升模拟IC设计效率。

weixin_30952535的博客 500

[MySQL进阶]——深入了解锁

锁的分类 操作类型 1) 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响。 2) 写锁(排它锁):当前操作没有完成之前,它会阻断其他写锁和读锁。 锁的粒度 表级锁 表级别的S锁、X锁 LOCK TABLES t READ :InnoDB存储引擎会对表 t 加表级别的 S锁 。 LOCK TABLES t WRITE :InnoDB存储引擎会对表 t 加表级别的 X锁 。 不推荐使用 意向锁 意向共享锁(intention shared lock, IS):事务有意向对表

热爱编程的机械生的救赎 856

Deeplabcut实战:从数据标注到行为分析的全流程解析

本文详细解析了Deeplabcut从数据标注到行为分析的全流程实战技巧,包括项目创建、配置文件定制、智能帧提取、高效标注方法、模型训练优化以及多视角融合等高级应用。通过具体案例和实用参数配置,帮助研究者快速掌握这一行为分析工具,提升实验效率和准确性。

weixin_29057163的博客 247

next-key lock的规则

背景 参考:https://blog.youkuaiyun.com/wanger61/article/details/107817158 MySQL 后面的版本可能会改变加锁策略,所以这个规则只限于截止到现在的最新版本,即 5.x 系列<=5.7.24,8.0 系列 <=8.0.1,实验发现不同版本的差异主要是:索引范围查询的临界值的取舍有所不同。8.0范围查询加锁不包含临界值,7.0范围查询加锁包含临界值。 Net-Key Lock的规则 原则 1:加锁的基本单位是 next-key lock。nex

春哥个人学习笔记 5546

把收款差异从扯皮变成闭环:SAP S/4HANA Dispute Resolution 模块到底解决什么问题?

本文摘要: SAP S/4HANA的争议解决模块(Dispute Resolution)通过标准化流程管理企业应收争议,解决发票与收款差异问题。核心是创建可追踪的Dispute Case工单,明确处理角色(Processor和Manager),并与财务系统联动实现自动清账。系统支持手工/自动创建争议工单,通过动态默认值减少错误,并最终实现争议金额归零的闭环管理。文中给出制造业客户扣款的完整处理案例,并强调组织落地需统一权限与目录。最后提供ABAP代码示例展示如何通过BAPI创建争议工单。(150字)

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 168

【异常】API 速率限制报错问题全流程排查与解决指南 API rate limit reached. Please try again later.

摘要: 本文针对大模型API开发中常见的速率限制报错("API rate limit reached")提供全流程解决方案。首先解析该报错的本质是服务端对高频调用的保护机制,通过三步排查法确认根因:客户端请求频率超出账户阈值。提出三级解决方案:1)临时等待限流周期重置;2)升级付费账户提高调用配额;3)构建多模型容灾架构实现自动切换。建议生产环境优先采用容灾方案,通过备用API节点保障业务连续性,同时平衡不同模型服务的成本与性能。

奶爸的编程之路,也就一周冷个三天 1440

Innodb锁机制:Next-Key Lock 浅谈

因为会话1已经对id=8的记录加了一个X锁,由于是RR隔离级别,INNODB要防止幻读需要加GAP锁:即id=5(8的左边),id=11(8的右边)之间需要加间隙锁(GAP)。上面测试已经验证了这一点,根据索引的有序性,数据按照主键(name)排序,后面写入的[5,cz]([5,e]的左边)和[11,ja]([11,j]的右边)不属于上面的范围从而可以写入。该SQL语句锁定的范围是(5,8],下个下个键值范围是(8,11],所以插入5~11之间的值的时候都会被锁定,要求等待。如上面分析,那就可以解释了。

精选资讯文章 499

基于STM32、HAL库的RN8209D电能计量芯片驱动程序设计

/ 假设电压互感器变比为220V:0.5V,电流互感器变比为20A:5mA。// 电压ADC原始值。// 电流ADC原始值。// 有功功率(W)// 电能(kWh)

corlin6688的博客 472

看来,MySQL next-key lock 的 bug 并没有被修复!

前言 在上一篇文章《MySQL next-key lock 加锁范围是什么?》中已经介绍了主键索引的加锁范围,现在来回顾一下: 加锁时,会先给表添加意向锁,IX 或 IS; 加锁是如果是多个范围,是分开加了多个锁,每个范围都有锁;(这个可以实践下 id < 20 的情况) 主键等值查询,数据存在时,会对该主键索引的值加行锁 X,REC_NOT_GAP; 主键等值查询,数据不存在时,会对查询条件主键值所在的间隙添加间隙锁 X,GAP; 主键等值查询,范围查询时情况则比较复杂: 8.0.17 版本是前

程序员小航 273

DFT笔记 DC/AC mode与Func

DFT scan可以分为DC和AC两种,区别如下图。

m0_61544122的博客 5732

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

MySQL next-key lock 加锁范围总结

前言 三篇文章分别通过实际操作,介绍了主键、非主键唯一索引、普通索引、普通字段四个方面介绍了加锁的范围。 本篇文章再做一个总结。 data_locks select * from performance_schema.data_locks; LOCK_MODE LOCK_DATA 锁范围 X,REC_NOT_GAP 15 15 那条数据的行锁 X,GAP 15 15 那条数据之前的间隙,不包含 15 X 15 15 那条数据的间隙,包含 15 LOCK_MODE = X 是前

程序员小航 1046

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

MySQL 锁与死锁:行锁、间隙锁、Next-Key Lock 与排查手册

InnoDB 为保证事务语义会在索引上加锁,常见有记录锁、间隙锁和 Next-Key Lock。RR 下范围当前读/修改为了防止范围内插入破坏语义,可能加 Next-Key 锁导致锁范围变大。死锁通常来自锁顺序不一致或索引缺失导致锁集合扩大,线上先用 processlist/innodb status/performance_schema 找到阻塞链和死锁双方,再通过固定更新顺序、缩短事务、补索引来根治。Q:Next-Key Lock 是什么?Q:InnoDB 的锁锁在哪里?Q:死锁怎么降低概率?

alkamgg的博客 499

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

mysql 退化range锁_InnoDB这个将近20年的"bug"修复了

原标题:InnoDB这个将近20年的"bug"修复了有点意外,惊喜ing。0. 背景信息1. MySQL 8.0.18 以前是怎么加锁的2. MySQL 8.0.18 之后终于变天了0. 背景信息最近在课程中讲到InnoDB行锁时,讲到一个知识点InnoDB行锁规则上,有这样的一个原则:对有唯一属性的索引(主键/唯一索引)进行范围条件加锁时,向右遍历(假设是普通正序索引,而且不加ORDER BY ...

weixin_42190623的博客 252

MySQL InnoDB 锁机制深度解析:行锁、表锁、间隙锁、临键锁(Next-Key Lock)

本文系统讲解MySQL(InnoDB)中的四种常见锁机制:表锁、行锁、间隙锁和临键锁。表锁粒度最粗,会锁定整张表;行锁通过索引精确锁定单条记录,并发性能最佳;间隙锁锁定记录间的空隙防止幻读;临键锁结合记录锁和间隙锁,是RR隔离级别的默认策略。文章分析了每种锁的特点、适用场景和潜在风险,特别指出索引失效、范围查询加锁等常见性能陷阱,并给出避免锁冲突的实用建议,如使用精确条件、添加合适索引、采用Redis处理热点数据等。理解这些锁机制对优化数据库并发性能和避免死锁至关重要。

Knight 1539

InnoDB这个将近20年的"bug"修复了

有点意外,惊喜ing。0. 背景信息1. MySQL 8.0.18 以前是怎么加锁的2. MySQL 8.0.18 之后终于变天了0. 背景信息最近在课程中讲到InnoDB行锁时,讲到一...

老叶茶馆 1696

MySQL事务和锁

Mysql锁机制与实战:锁分类,行锁原理,悲观锁,乐观锁,死锁与解决方案

weixin_42298834的博客 1037

MySQL的加锁规则

加锁的规则:一个原则:加锁的基本单位是 next-key lock。加锁是对索引加锁的。一个bug:非唯一索引范围查询,该索引的临键锁不会有退化为间隙锁或记录锁。其他就通过常理来分析推理即可。唯一索引等值查询唯一索引范围查询非唯一索引等值查询非唯一索引范围查询非索引查询。

m0_57408211的博客 1663

MySQL RC隔离级别惊现间隙锁:是bug吗?

所以当前的实现如果gap 上存在至少一个相同的record(包括删除但是还没被回收的记录,因为删除只是做了个删除的墓碑标识,后面再回收), 那么需要给整个range 都加上gap X lock, 加了gap X lock 以后就可以禁止其他事务在这个gap 区间插入数据, 也就是通过lock 来保证第一步和第二步的原子性.最后还得再<5,5> 上增加gap 锁(假如这个5,5变成很大,那么意味着锁的gap 会非常大,影响并发性能),以防止<3,19>之后的数据被插入。死锁的日志是这样子的。

mxt51220的博客 1136

MySQL普通索引的加锁

前言 前面已经介绍了主键索引的加锁范围和非主键唯一索引的加锁范围。 主键索引: 加锁时,会先给表添加意向锁,IX 或 IS; 加锁是如果是多个范围,是分开加了多个锁,每个范围都有锁;(这个可以实践下 id < 20 的情况) 主键等值查询,数据存在时,会对该主键索引的值加行锁 X,REC_NOT_GAP; 主键等值查询,数据不存在时,会对查询条件主键值所在的间隙添加间隙锁 X,GAP; 主键等值查询,范围查询时情况则比较复杂: 8.0.17 版本是前开后闭,而 8.0.18 版本及以后,修改为了前

程序员小航 2285

聊聊 mysql 中的加锁

聊聊 mysql 中的加锁

yeyinzhu3211的专栏 2455

mysql range锁,MySQL唯一索引范围锁bug修复

commit d1b0afd75ee669f54b70794eb6dab6c121f1f179Author: Jakub Łopuszański Date: Wed Jul 17 16:34:01 2019 +0200Bug #29508068 UNNECESSARY NEXT-KEY LOCK TAKENWhen doing a SELECT...FOR [SHARE|UPDAT...

weixin_42356162的博客 318
上一篇: 「Linux」进程间通信---共享内存
下一篇: 图解 SQL,这也太形象了吧
JavaMonsterr
博客等级 码龄4年 211粉丝 481原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值