MySQL InnoDB 加锁全解析

这篇博客将基于 MySQL 5.7 及 8.0(8.0.13 及以下版本)的 InnoDB 引擎,深入解析可重复读(Repeatable Read, RR)隔离级别下的加锁逻辑。

在 RR 隔离级别下,InnoDB 为了解决幻读问题,引入了 Next-Key Lock(记录锁 + 间隙锁)。理解其加锁范围,需要遵循以下核心准则:

核心加锁准则

  • 原则 1 :加锁的基本单位是 Next-Key Lock ,区间为前开后闭 (prev, curr]
  • 原则 2 :只有查找过程中访问到的对象才会加锁。
  • 优化 1 :索引等值查询,给唯一索引 加锁时,Next-Key Lock 退化为行锁(记录需存在)。
  • 优化 2 :索引等值查询,向右遍历且最后一个值不满足等值条件时,Next-Key Lock 退化为间隙锁
  • Bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。

1. 等值查询:唯一索引与非唯一索引

假设表 t 中已有数据 ID(或索引 c):0, 5, 10, 15, 20, 25。

唯一索引等值查询

  • 记录存在where id=10 for update
    • 根据原则 1,初始锁为 (5, 10]
    • 根据优化 1,退化为 行锁 id=10
  • 记录不存在where id=7 for update
    • 根据原则 1,定位到 10,初始锁为 (5, 10]
    • 根据优化 2,10 不满足等值条件,退化为 间隙锁 (5, 10)

非唯一索引等值查询

  • 示例select id from t where c=5 lock in share mode;
    • 步骤 1:定位到 c=5,加 Next-Key Lock (0, 5]
    • 步骤 2:非唯一索引需向右遍历确认是否有更多 5,访问到 10。
    • 步骤 3:根据原则 2,10 被访问,加锁 (5, 10]
    • 步骤 4:根据优化 2,10 不满足等值条件,退化为 间隙锁 (5, 10)
  • 注意 :若是覆盖索引查询且无 for update,主键索引不加锁。

2.范围查询:唯一索引与非唯一索引

InnoDB 范围查询加锁:三步分析法

第一步:确定查询方向(Direction)
  • ASC(升序):由左向右扫描。
  • DESC(降序):由右向左扫描。
第二步:确定查询起点(Starting Point)
  • 动作 :通过 B+ 树搜索(Tree Search) 定位到范围内的第一行。
  • 加锁 :根据原则 2(访问即加锁) ,引擎首次访问的该行必须加上 Next-Key Lock (区间为 (prev,curr](prev, curr](prev,curr])。
第三步:确定查询终点(Ending Point)
  • 判定 :迭代器持续步进,直到读取到第一个不满足条件的记录(负例)。
  • 加锁 :根据原则 2,该"负例"记录因被访问比对,必须加上 Next-Key Lock
  • 唯一索引 Bug:即便在唯一索引上,范围查询也会因 Bug 访问到不满足条件的第一个值为止并加锁。

示例 1:非唯一索引,c >= 15 ORDER BY DESC

假设索引 c 的值为:0, 5, 10, 15, 20, 25, 30。

  1. 确认方向DESC 声明了由右向左(由大到小)的逆向扫描。
  2. 确认起点 :条件是 c >= 15,在逆向扫描中,起点是索引的逻辑最大值 Supremum
  3. 确认终点
    • 扫描器向左读取 30, 25, 20, 15,均符合条件。
    • 由于是非唯一索引,引擎不知道当前的 15 是不是最左侧的 15。
    • 为了确认边界,引擎必须继续向左读取,访问到 10
    • 对比发现 10<1510 < 1510<15,扫描终止。
  4. 加锁结果
    • 根据"访问即加锁"原则,被"摸到"的记录都要加锁。
    • 范围查询不触发"退化为间隙锁"的优化。
    • 最终锁住 :Supremum 记录、30, 25, 20, 15,以及 **10 的 Next-Key Lock (5, 10]**

示例 2:非唯一索引,c >= 15 ORDER BY ASC

  1. 确认方向ASC(默认)声明了由左向右(由小到大)的正向扫描。
  2. 确认起点 :树搜索定位第一个满足条件 c >= 15 的记录,即第一个 15
  3. 确认终点 :条件只有下限没有上限,引擎会一直向右扫描,直到索引的最末端 Supremum 记录。
  4. 加锁结果
  • 从 15 开始,向右访问到的所有记录及其间隙均加锁。
  • 最终锁住 :15, 20, 25, 30 的 Next-Key Lock 以及 Supremum 的 Next-Key Lock

示例 3:唯一索引,c >= 15 ORDER BY DESC

这里触及了 PDF 中提到的那个关键 Bug

  1. 确认方向DESC 逆向扫描。
  2. 确认起点 :索引逻辑最大值 Supremum
  3. 确认终点
    • 虽然是唯一索引,理论上读到 15 就可以停止。
    • 但根据 规则 5(Bug):唯一索引上的范围查询会访问到不满足条件的第一个值为止。
    • 引擎依然会向左多读取一个 10
  4. 加锁结果
    • 因为扫描器"多看了一眼" 10,根据原则 2,10 必须加锁。
    • 由于是范围查询,10 的锁不会退化。
    • 最终锁住 :Supremum, 30, 25, 20, 15,以及 **10 的 Next-Key Lock (5, 10]**

3. 无索引查询:性能灾难

WHERE 条件字段没有索引时(如 update t set d=d+1 where c=7):

  1. 全表扫描:由于没有索引加速,引擎被迫扫描主键索引(聚簇索引)的所有记录。
  2. 全面封锁 :根据原则 2,扫描过程中访问到的每一行记录及其之间的间隙都会被加上 Next-Key Lock
  3. 结果 :这等同于锁住了整张表的所有行和所有间隙。在事务提交前,任何其他事务对该表的 INSERTUPDATEDELETE 都会被阻塞。
相关推荐
研究员子楚2 小时前
GEO行业发展标准体系白皮书V2.0-第09卷 · 适配篇:硅基主权伦理宪章与行业适配宣言
数据库·人工智能·geo
CoderMeijun3 小时前
一条SQL查询语句在MySQL中是如何执行的
mysql·sql执行流程·连接器·mysql架构·索引下推·执行器
l134062082354 小时前
鸿蒙实战:RDB 数据库设计与 DatabaseService
数据库·华为·harmonyos·鸿蒙系统
山峰哥4 小时前
数据库工程与索引策略实战指南‌
服务器·数据库·sql·oracle·深度优先
谙忆4 小时前
图片版权保护工程实战:明水印、盲水印(DCT隐写)、Referer防盗链与签名URL怎么落地
数据库
KaMeidebaby4 小时前
卡梅德生物技术快报|抗体合成:多肽抗体合成工程化方案:Nsp2 保守肽多抗制备与多维度验证
前端·网络·数据库·人工智能·算法
xfhuangfu4 小时前
19c CDB架构下的数据库启动和停止
数据库·架构
神道君天下5 小时前
填充与积累:积分与面积的可视化
数据库·oracle
爱折腾的小黑牛5 小时前
小程序云开发数据模型设计:礼账系统的三表结构
数据库·架构
爱折腾的小黑牛5 小时前
礼账小程序的数据安全方案:加密与备份
数据库·安全