Mysql的锁退化
MySQL 的锁退化,本质是 InnoDB 在特定条件下,将锁定范围更大的锁(如临键锁、间隙锁)自动降级为锁定范围更小的锁(如行锁),以减少锁冲突、提升并发性能,核心场景与索引匹配和事务隔离级别相关。
- 核心触发条件:精准匹配唯一索引
InnoDB 的行锁基于索引实现,锁的类型(临键锁/间隙锁/行锁)由“索引类型”和“查询条件匹配方式”决定,当满足以下条件时会发生锁退化:
- 索引要求:查询使用 主键索引 或 唯一二级索引(索引值唯一,无重复)。
- 匹配要求:查询条件是 “= ”或“IN”(且匹配到明确的单行数据),即“精准定位到某一行”,而非范围匹配(如 > , < , BETWEEN )。
- 典型场景:从临键锁退化为行锁
默认事务隔离级别(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 对应的行,不锁定间隙。
- 不发生锁退化的反例
若不满足“唯一索引精准匹配”,锁会保持原类型(临键锁/间隙锁),不会退化:
- 非唯一索引:用普通二级索引(如 age ,值可重复)查询,即使 WHERE age = 20 ,也会加临键锁(锁定 age=20 的行+前后间隙)。
- 范围匹配:用唯一索引但条件是范围(如 id > 10 ),会加临键锁(锁定 id>10 的所有行+间隙)。
- 未匹配到数据:用唯一索引查询但无对应数据(如 id=99 不存在),会加 间隙锁(锁定 id 所在的间隙),而非行锁。
- 锁退化的本质目的
InnoDB 设计锁退化,是在“保证事务隔离性(防止幻读)”和“提升并发性能”之间的平衡:
- 若保留临键锁,会锁定多余间隙,导致其他事务无法插入/更新间隙内数据,增加锁冲突;
- 退化為行锁后,仅锁定必要的数据行,既保证了当前事务对目标行的独占性,又最小化了对其他事务的影响。
在 MySQL 默认的 Repeatable Read(可重复读)隔离级别 下,使用非唯一索引查询不会发生幻读,核心原因是 InnoDB 通过“临键锁(Next-Key Lock)”锁定了“匹配行+间隙”,从物理层面阻断了其他事务插入“能被当前查询匹配的新数据”的可能。
- 先明确:幻读的本质是“同一事务内,两次相同查询返回行数不同”
幻读的触发条件是:事务 A 先执行一次查询(如 SELECT * FROM user WHERE age = 20 ),事务 B 此时插入一行 age=20 的新数据并提交,事务 A 再次执行相同查询时,结果集中多了一行数据——这就是“幻读”。
要避免幻读,关键是 阻止事务 B 在事务 A 查询期间,插入“能被 A 的查询条件匹配”的新数据。
- 非唯一索引查询时,临键锁如何“防插入”?
非唯一索引(如 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 再次查询时,结果行数和第一次完全一致,自然就不会发生幻读。
- 关键总结:非唯一索引防幻读的核心逻辑
非唯一索引查询之所以不发生幻读,本质是 临键锁的“间隙锁”部分,封锁了“所有可能插入新匹配数据的区间”,从源头杜绝了“新增匹配行”的可能,而非仅锁定已存在的行。
这与“唯一索引精准匹配时锁退化为行锁”不同——非唯一索引因值可能重复,必须锁定间隙才能避免幻读,而唯一索引精准匹配时无重复值,无需锁间隙即可保证无新行插入。