当前位置: 首页 > news >正文

Mysql是怎么加锁的?

原文地址https://www.xiaolincoding.com/mysql/lock/how_to_lock.html#%E4%BB%80%E4%B9%88-sql-%E8%AF%AD%E5%8F%A5%E4%BC%9A%E5%8A%A0%E8%A1%8C%E7%BA%A7%E9%94%81

我只是精简一下做个记录

这篇汇总将基于MySQL 8.0+ 的 InnoDB 引擎,在可重复读(Repeatable Read, RR)隔离级别下进行深度的全场景拆解。

核心底座:加锁准则
  • 原则:加锁的基本单位是Next-Key Lock(临键锁),区间是 (前一个记录, 当前记录]。

  • 退化:为了提高并发,MySQL 会在确保“不产生幻读”的前提下,将临键锁降级为Record Lock(记录锁)Gap Lock(间隙锁)

  • 查找:索引查找过程中,扫描到哪里,锁就加到哪里。

准备环境
  • 表:user,主键 id,普通索引 age,无索引字段 name。

  • 数据:(1,10), (5,20), (10,30), (15,40), (20,50)


一、 唯一索引(主键)加锁逻辑

1. 等值查询
  • 记录存在(如 id=5):

    • 锁:记录锁 id=5。

    • 原因:唯一性保证了不可能有第二个 id=5 插入,只需锁住这一行,不需要锁间隙(最极致的退化)。

  • 记录不存在(如 id=7):

    • 锁:间隙锁 (5, 10)。

    • 原因:锁是加在索引上的,既然 7 这条记录不存在,没法直接锁行。扫描到 10 时发现大于 7,为了防止别人插入 7,只能把 5~10 之间的“物理空地”锁住。放过 10 是因为 10 本身与 7 无关。

2. 范围查询(>、>=、<、<=)
  • id > 5:

    • 锁:(5, 10]、(10, 15]、(15, 20]、(20, +supremum]。(注:supremum是无穷大伪记录)

    • 原因:所有的 Next-Key Lock 都不降级,确保整个范围及后续间隙无法插入。

  • id >= 5:

    • 锁:记录锁 id=5 + (5, 10],(10, 15] ...

    • 原因:id=5 是等值匹配且唯一,所以 5 这一处精准降级为记录锁,后面的维持临键锁。

  • id < 15:

    • 锁:(-∞, 1]、(1, 5]、(5, 10]、间隙锁 (10, 15)。

    • 注意:MySQL 扫描到 15 时发现不满足 < 15,于是最后的临键锁 (10, 15]退化为间隙锁 (10, 15)。既防止了 15 之前的间隙被插入,又释放了对 15 本身的锁定。

  • id <= 15:

    • 锁:(-∞, 1]、(1, 5]、(5, 10],(10, 15])。

    • 原因:因为 15 在查询范围内必须被锁住,同时为了防止 10~15 之间的间隙被插入数据,必须保持完整的临键锁,不发生降级!


二、 非唯一索引(普通索引)加锁逻辑

这是最容易搞混的地方,因为它需要同时保护二级索引树和主键聚簇索引树

1. 等值查询
  • 记录存在(如 age=20):

    • 锁:age 索引上加 (10, 20] 的临键锁,并且额外加 (20, 30) 的间隙锁。

    • 为什么?非唯一索引必须把 20 左右两边的物理过道都封死,否则别人可以插入一个新的 age=20(只要主键不同即可),导致幻读。

    • 回表:对应的 id=5 主键索引上会加一个记录锁。

  • 记录不存在(如 age=25):

    • 锁:age 索引上加 (20, 30) 间隙锁。

    • 原因:退化逻辑与唯一索引未命中时一致,只需守住这块无人区。

2. 范围查询(>、>=、<、<=)
  • 年龄 > 20:

    • 锁:age 索引上 (20, 30]、(30, 40]、(40, 50]、(50, +supremum]。

    • 规则:非唯一索引的范围查询保护极严,全是 Next-Key Lock,基本不降级。

  • 年龄 >= 20:

    • 锁:age 索引上 (10, 20],(20, 30] ...

    • 区别:即使是等值部分,非唯一索引也不会像主键那样降级为记录锁,因为 20 前面的间隙也得守住,防止插队。


三、 各种符号的“退化”总结表 (修正版)

场景索引类型匹配结果最终锁范围备注
等值 (=)唯一存在记录锁最极致的性能降级,精准打击
等值 (=)唯一不存在间隙锁退化,仅锁住目标所在的真空地带
等值 (=)非唯一存在Next-Key + 随后的 Gap双重保护,前后过道全部封死
范围 (>=)唯一包含边界边界记录锁 + 后续 Next-Key起点等于精确查找,起点降级
范围 (>)唯一-全程 Next-Key不降级
范围 (<)唯一不含边界前置 Next-Key + 最后的 Gap扫到第一个不符合条件的边界,边界退化为间隙锁
范围 (<=)唯一包含边界全程 Next-Key因为要锁住边界又要保护间隙,不降级
所有范围非唯一-全程 Next-Key保护极严,绝不降级

四、 没加索引的查询(全表封杀)

SQL:更新用户设置年龄=100 其中 name = '张三';

  • 过程:

    1. InnoDB 发现 name 没索引,只能走聚簇索引(主键)进行全表扫描。

    2. 扫描时,每一行记录都会被加上Next-Key Lock

    3. 结果是:(-∞, 1]、(1, 5]、(5, 10] ...直到 (+∞, supremum]。

  • 后果:

    • 这意味着整张表无论是修改已有行还是插入新行,全被锁死!这就是俗称的“锁表”。

  • 补充注意:MySQL 内部有优化,在某些版本中,如果扫描后发现不匹配,会提前释放掉不符合条件的锁。但在高并发瞬间,全表扫描依然可能导致大规模锁等待或死锁。


五、 终极恍然大悟:为啥要这么变?

  • 为啥唯一索引等值存在就降级记录锁?

    • 因为主键 ID 是唯一的,只要我锁住了 ID=5 这一行,全世界都不可能再产生第二个 ID=5。所以我不需要管 (1, 5) 的间隙。

  • 为啥非唯一索引等值还要加间隙锁?

    • 因为你有 age=20,我再插一个 age=20(主键设为 99)是可以成功的。如果不锁间隙,你第二次查 age=20 就会多出一行,这就是“幻读”。

  • 为啥范围查询很少降级?

    • 范围查询的目标通常是保护“一片领土”。如果你查 id > 5,哪怕 5 后面的 6, 7, 8 现在不存在,我也得防着你,所以必须把后面所有的间隙全焊死。

🔥 一句话总结:
能降级(退化)的前提是——即使我放开了间隙,你也绝对无法在我的眼皮底下插进一行符合我查询条件的“幻影”数据。如果不能保证这一点,MySQL 就会死死握住 Next-Key Lock 不撒手。

http://www.jsqmd.com/news/541864/

相关文章:

  • Ghidra逆向工程工具:5分钟快速安装与新手入门完整指南
  • 魔兽世界怀旧服宏命令全解析:从自动换装到智能判定,老玩家才知道的黑科技
  • MyBatis 中 CDATA 的实战应用与避坑指南
  • 【算法系列】非线性最小二乘-高斯牛顿法在SLAM中的高效应用
  • 开源 AI 应用平台实战部署:从零搭建到插件调试避坑指南
  • 无人机新手必看:从选购到飞行,避开这些坑才能玩得爽
  • 不只是改权限:深入理解zsh的compinit安全机制与compaudit的实战用法
  • 3个核心价值:bilibili-api的API开发与数据接口应用
  • Delphi XE在Linux上开发桌面应用:从安装FMXLinux插件到第一个跨平台GUI程序
  • NVIDIA Profile Inspector:解锁显卡隐藏性能的终极指南
  • C++ 模板与泛型编程入门
  • 如何快速掌握ERPNext自动化部署:终极实用指南
  • 告别手动!用Python脚本+Autodock Vina搞定多对多分子对接与热图绘制(附完整代码)
  • 嵌入式TCP行协议解析库TcpLineStream设计与应用
  • 嵌入式开发必备:用嘉立创EDA设计双层PCB板的7个高效布线技巧
  • 三层架构形象理解
  • ESP32 FreeRTOS任务状态全解析:从就绪态到挂起态的完整生命周期管理
  • 实战指南:如何用SG-LLIE Transformer模型提升夜间照片质量(附代码调参技巧)
  • 嵌入式开发板选型:需求、预算与扩展性平衡
  • 从DIY电钻到航模电调:CW32L010 ESC Driver套件实战应用解析
  • 低通与高通滤波器的电路设计与相位补偿实战解析
  • MonkeyCode AI开发平台上线:注册免费送2万点算力!!默认免费使用MiniMax2.7!!
  • 单电阻采样的永磁同步电机相电流重构策略仿真:解锁优秀波形效果
  • 【STM32实战技巧】- 玩转EC11编码器:从GPIO轮询到TIM编码器模式
  • Android 基于ViewPager2+ExoPlayer+VideoCache 打造短视频无缝预加载方案
  • Arduino OPL2库:嵌入式平台精准驱动YM3812/YMF262 FM合成芯片
  • 避坑指南:Apollo绕行逻辑调试中,path_assessment_decider.cc排序修改的‘是与非’
  • 实战指南:从零到一,用Miniedit构建可编程网络拓扑
  • 别再死磕单频点了!用ADS负载牵引搞定宽带功放匹配的实战思路(以CGH40010F为例)
  • 快速上手:利用快马ai一键生成openclaw在windows的部署原型