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

Mysql的锁退化

MySQL 的锁退化,本质是 InnoDB 在特定条件下,将锁定范围更大的锁(如临键锁、间隙锁)自动降级为锁定范围更小的锁(如行锁),以减少锁冲突、提升并发性能,核心场景与索引匹配和事务隔离级别相关。

  1. 核心触发条件:精准匹配唯一索引

InnoDB 的行锁基于索引实现,锁的类型(临键锁/间隙锁/行锁)由“索引类型”和“查询条件匹配方式”决定,当满足以下条件时会发生锁退化:

  • 索引要求:查询使用 主键索引 或 唯一二级索引(索引值唯一,无重复)。
  • 匹配要求:查询条件是 “= ”或“IN”(且匹配到明确的单行数据),即“精准定位到某一行”,而非范围匹配(如 > , < , BETWEEN )。
  1. 典型场景:从临键锁退化为行锁

默认事务隔离级别(Repeatable Read,可重复读)下,InnoDB 对行查询默认加 临键锁(Next-Key Lock,行锁+间隙锁的组合),用于防止幻读;但满足“唯一索引精准匹配”时,会退化:

  • 场景1:主键索引精准匹配
    表 user 主键为 id ,执行 SELECT * FROM user WHERE id = 10 FOR UPDATE :
    因 id 是主键(唯一索引),且条件 id=10 精准匹配单行,临键锁会退化為 排他行锁(Record Lock),仅锁定 id=10 这一行,不锁定周围间隙。
  • 场景2:唯一二级索引精准匹配
    表 user 有唯一二级索引 phone ,执行 SELECT * FROM user WHERE phone = ‘13800138000’ FOR UPDATE :
    因 phone 是唯一索引,且条件精准匹配单行,临键锁会退化為 行锁,仅锁定该 phone 对应的行,不锁定间隙。
  1. 不发生锁退化的反例

若不满足“唯一索引精准匹配”,锁会保持原类型(临键锁/间隙锁),不会退化:

  • 非唯一索引:用普通二级索引(如 age ,值可重复)查询,即使 WHERE age = 20 ,也会加临键锁(锁定 age=20 的行+前后间隙)。
  • 范围匹配:用唯一索引但条件是范围(如 id > 10 ),会加临键锁(锁定 id>10 的所有行+间隙)。
  • 未匹配到数据:用唯一索引查询但无对应数据(如 id=99 不存在),会加 间隙锁(锁定 id 所在的间隙),而非行锁。
  1. 锁退化的本质目的

InnoDB 设计锁退化,是在“保证事务隔离性(防止幻读)”和“提升并发性能”之间的平衡:

  • 若保留临键锁,会锁定多余间隙,导致其他事务无法插入/更新间隙内数据,增加锁冲突;
  • 退化為行锁后,仅锁定必要的数据行,既保证了当前事务对目标行的独占性,又最小化了对其他事务的影响。

在 MySQL 默认的 Repeatable Read(可重复读)隔离级别 下,使用非唯一索引查询不会发生幻读,核心原因是 InnoDB 通过“临键锁(Next-Key Lock)”锁定了“匹配行+间隙”,从物理层面阻断了其他事务插入“能被当前查询匹配的新数据”的可能。

  1. 先明确:幻读的本质是“同一事务内,两次相同查询返回行数不同”

幻读的触发条件是:事务 A 先执行一次查询(如  SELECT * FROM user WHERE age = 20 ),事务 B 此时插入一行  age=20  的新数据并提交,事务 A 再次执行相同查询时,结果集中多了一行数据——这就是“幻读”。

要避免幻读,关键是 阻止事务 B 在事务 A 查询期间,插入“能被 A 的查询条件匹配”的新数据。

  1. 非唯一索引查询时,临键锁如何“防插入”?

非唯一索引(如  age ,允许重复值)的查询,InnoDB 不会只锁“已匹配的行”,而是会触发 临键锁——它是“行锁 + 间隙锁”的组合,锁定范围包含“所有已匹配的行”和“这些行前后的间隙”。

举个具体例子:
假设表  user  的  age  列是普通索引,现有数据的  age  值为 [18, 20, 20, 22],对应的索引间隙(可理解为“数据之间的空白区间”)为:

  • (-∞, 18)、(18, 20)、(20, 20)、(20, 22)、(22, +∞)

当事务 A 执行  SELECT * FROM user WHERE age = 20 FOR UPDATE (非唯一索引查询)时:

  • InnoDB 会对 所有  age=20  的行加行锁(阻止其他事务修改/删除这些行);
  • 同时对  age=20  相关的间隙加间隙锁,即锁定 (18, 20)、(20, 20)、(20, 22) 这三个间隙。

此时,事务 B 若想插入  age=20  的新数据,无论插入到哪个位置(如 18 和 20 之间、两个 20 之间、20 和 22 之间),都会被“间隙锁”阻塞,无法成功插入。

既然事务 B 插不进新数据,事务 A 再次查询时,结果行数和第一次完全一致,自然就不会发生幻读。

  1. 关键总结:非唯一索引防幻读的核心逻辑

非唯一索引查询之所以不发生幻读,本质是 临键锁的“间隙锁”部分,封锁了“所有可能插入新匹配数据的区间”,从源头杜绝了“新增匹配行”的可能,而非仅锁定已存在的行。

这与“唯一索引精准匹配时锁退化为行锁”不同——非唯一索引因值可能重复,必须锁定间隙才能避免幻读,而唯一索引精准匹配时无重复值,无需锁间隙即可保证无新行插入。

http://www.dtcms.com/a/365670.html

相关文章:

  • 虚拟机+ubuntu+docker+python部署,以及中途遇到的问题和解决方案
  • 计算机科学领域-CS基础
  • 信创MySQL到达梦数据库的SQL语法转换技术解析
  • 使用Java定时爬取CSDN博客并自动邮件推送
  • CPU和GPU的区别与作用域
  • prometheus+grafana搭建
  • 虚拟机NAT模式通过宿主机(Windows)上网不稳定解决办法(无法上网)(将宿主机设置固定ip并配置dns)
  • 【面试题】OOV(未登录词)问题如何解决?
  • Unity 枪械红点瞄准器计算
  • K8S 部署 NFS Dynamic Provisioning(动态存储供应)
  • Grafana可视化平台深度解析:选型、竞品、成本与资源消耗
  • SpringCloud整合分布式事务Seata
  • C语言(长期更新)第13讲:指针详解(三)
  • 毒蛇品种检测识别数据集:12个类别,6500+图像,全yolo标注
  • 印度股票数据API对接文档
  • 硬件(一)51单片机
  • 【和春笋一起学C++】(三十九)类作用域
  • [鸿蒙心迹]带新人学鸿蒙的悲欢离合
  • “企业版维基百科”Confluence
  • Docker实战指南:从安装到架构解析
  • 【QT特性技术讲解】QPrinter、QPdf
  • leetcode 38 外观数列
  • 联想开天X7:携手海光,开启信创PC高性能新时代
  • Java中 String、StringBuilder 和 StringBuffer 的区别?
  • WHAT - 协程及 JavaScript 具体代码示例
  • PgManage:一款免费开源、跨平台的数据库管理工具
  • Packet Radio Network,PRNET
  • 从发现到恢复,看瑞数信息如何构建“抗毁重构”实战路径
  • VR节约用水模拟体验系统:沉浸式体验如何改变我们的用水习惯
  • 全员0门槛数据分析:纷享销客BI Agent,让数据价值直抵业务