news 2026/4/19 22:49:08

从S锁/X锁到Next-Key Lock:MySQL锁机制硬核拆解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从S锁/X锁到Next-Key Lock:MySQL锁机制硬核拆解

从 S 锁 / X 锁 到 Next-Key Lock:MySQL InnoDB 锁机制硬核拆解

MySQL 的 InnoDB 引擎锁机制是面试和生产中高频考点,尤其是幻读如何被解决、Next-Key Lock到底锁了什么、加锁规则如何判断等。下面从基础到进阶,一层层拆解。

1. 锁的分类总览(InnoDB 核心)

InnoDB 锁主要分三层:

  • 表级锁(意向锁为主)
  • 行级锁(最重要,Record / Gap / Next-Key)
  • 页级锁(基本不用)

重点关注行级锁的三种形态 + 两种模式:

锁类型英文名模式(Mode)作用是否允许其他事务读写
共享锁Shared LockS允许读,不允许写其他事务可加 S,不允许加 X
排他锁Exclusive LockX允许读 + 写,不允许其他事务读写其他事务都阻塞

S/X 是锁的“权限”,而 Record/Gap/Next-Key 是锁的范围

2. 三种行锁范围(锁住什么)

锁名称英文名锁住范围典型场景是否防幻读
记录锁Record Lock仅仅锁住一条索引记录本身唯一索引 + 等值查询(主键/唯一索引)×
间隙锁Gap Lock锁住索引记录之间的间隙(不含记录本身)防止在间隙中插入新记录√(部分)
临键锁Next-Key Lock记录 + 它前面的间隙(左开右闭)InnoDB RR 隔离级别下默认加的锁√(最强)

关键记忆

  • Next-Key Lock =Record Lock + Gap Lock
  • 锁定形式:(gap 前) ← record → (gap 后)→ 但实际是前开后闭,锁住 record 本身 + record前面的间隙

3. S/X 与 Record/Gap/Next-Key 的组合

  • S 型 Record Lock:共享记录锁
  • X 型 Record Lock:排他记录锁
  • S 型 Gap Lock:共享间隙锁(较少见)
  • X 型 Gap Lock:排他间隙锁(最常见防插入)
  • S 型 Next-Key Lock:共享临键锁(SELECT … LOCK IN SHARE MODE)
  • X 型 Next-Key Lock:排他临键锁(SELECT … FOR UPDATE / UPDATE / DELETE 默认)

4. 为什么需要 Gap Lock 和 Next-Key Lock?——幻读的根源

幻读定义(ANSI SQL 标准):
同一事务内,前后两次范围查询,结果集行数不同(主要是插入导致)。

快照读(普通 SELECT)靠 MVCC 解决不可重复读,但无法防插入→ 因此防不了幻读。

当前读(SELECT … FOR UPDATE / LOCK IN SHARE MODE / UPDATE / DELETE)会加锁。

InnoDB 在Repeatable Read(默认隔离级别)下,通过Next-Key Lock实现当前读防幻读

5. Next-Key Lock 加锁规则(面试最爱问)

核心规则(InnoDB RR 级别下):

  1. 加锁对象:走的是哪个索引,就在哪个索引上加锁(聚簇索引 / 辅助索引)
  2. 默认使用Next-Key Lock(前开后闭)
  3. 唯一索引 + 等值命中退化为 Record Lock(优化)
  4. 非唯一索引 / 范围查询→ 保持Next-Key Lock
  5. 等值查询命中多条→ 最后一跳退化为 Gap Lock(右边界不锁记录)
  6. 插入意向锁(Insert Intention Lock):插入时会在间隙中加的意向锁,不阻塞其他插入意向锁,但会被普通 Gap Lock 阻塞

经典例子(假设表 t 有字段 id 主键,c 非唯一索引)

CREATETABLEt(idINTPRIMARYKEY,cINT,KEYidx_c(c));INSERTINTOtVALUES(5,10),(10,20),(15,30);

事务 A

BEGIN;SELECT*FROMtWHEREc=15FORUPDATE;

加锁分析

  • 在 idx_c 上找到 c=15 的记录
  • X 型 Next-Key Lock(10, 15]
    • 锁住 15 这条记录(Record Lock)
    • 锁住 (10,15) 的间隙(Gap Lock)

事务 B能做什么?

  • INSERT c=12 → 被阻塞(落入间隙)
  • INSERT c=15 → 被阻塞(记录本身被 X 锁)
  • INSERT c=20 → 可插入(不在范围内)
  • UPDATE c=15 的行 → 被阻塞
  • SELECT c=15 的行 → 普通读可(MVCC),当前读阻塞

6. 常见场景加锁总结表

操作类型隔离级别索引情况加锁类型(默认)防幻读?
SELECT … FOR UPDATERR非唯一索引 + 范围Next-Key Lock
SELECT … FOR UPDATERR唯一索引 + 等值命中Record Lock否(但当前读安全)
UPDATE / DELETERR非唯一索引Next-Key Lock
INSERT--插入意向锁(不互斥插入)-
SELECT … (快照读)RR / RC-无锁(MVCC)
RC 隔离级别RC-仅 Record Lock

7. 为什么 RC 不防幻读,而 RR 能防?

  • RC:每次当前读都生成新 ReadView + 只加 Record Lock → 允许其他事务插入 → 幻读可能
  • RR:事务开始后 ReadView 固定 + 默认 Next-Key Lock → 范围读时锁住间隙 → 防插入 → 防幻读

8. 生产中常见“坑”

  • 唯一索引降级 Record Lock → 无法完全防幻读(但当前读本身安全)
  • 死锁常见:Next-Key Lock 之间互斥(尤其是非唯一索引范围操作)
  • 大事务 + 范围 FOR UPDATE → 容易锁全表(或很大范围)
  • 降级方案:用READ COMMITTED(关闭 Next-Key Lock)+ 业务补齐防重

一句话总结:

InnoDB 通过S/X 控制读写权限,用Record Lock 锁行,用Gap Lock 锁间隙,最终用Next-Key Lock(Record + 前间隙)在RR 隔离级别下实现了当前读防幻读,这是 MySQL 与传统数据库在隔离级别实现上的最大区别之一。

想看具体死锁案例、锁等待分析(information_schema)、还是间隙锁导致的性能问题优化?可以继续深挖~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 16:31:38

day80(2.8)——leetcode面试经典150

22. 括号生成 22. 括号生成 题目&#xff1a; 题解&#xff1a; class Solution {int left; //左括号int right; //右括号List<String> res;void dfs(int l, int r, StringBuilder sb) {if(l0&&r0) {res.add(sb.toString());return ;}if(l>0) {sb.appen…

作者头像 李华
网站建设 2026/4/17 12:29:39

从零开始写算法——贪心篇2:买卖股票的最佳时间 + 划分字母区间

在算法中&#xff0c;贪心算法 (Greedy Algorithm) 往往是一个让人又爱又恨的话题。爱它是因为代码通常很短&#xff0c;恨它是因为“当前最优选择会导致全局最优”这个逻辑有时候很难一眼看穿。今天我们通过两道经典的 LeetCode 题目——121. 买卖股票的最佳时机 和 763. 划分…

作者头像 李华
网站建设 2026/4/17 16:58:19

【小程序毕设源码分享】基于springboot+小程序的汽车服务企业客户评价APP的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/17 14:38:02

【小程序毕设全套源码+文档】基于Android的地球村共享书屋平台的设计与实现(丰富项目+远程调试+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/19 17:26:30

Redis Zset的实现为什么用跳表,而不用平衡树?

之前写过一篇 Redis 数据类型的底层数据结构的实现&#xff0c;其中提到&#xff0c;ZSet 对象的底层数据结构实现之一是跳表。 然后&#xff0c;有读者就问&#xff1a;为什么不使用平衡树&#xff08;如红黑树、AVL 树&#xff09;&#xff1f; 我们先来了解下跳表&#xf…

作者头像 李华
网站建设 2026/4/18 16:21:30

2026之初凭这份Java面试突击指南,斩获9张大厂Offer

今年金三银四快要到了&#xff0c;不知道大家都拿到Offer没有&#xff0c;如果没有的话&#xff0c;希望大家不要怪LZ凡尔赛了&#xff08;手动狗头&#xff09;。LZ截止今天为止已经收到了第9家公司的Offer&#xff0c;这张的Offer的话给到28k*14薪。由于个人原因&#xff0c;…

作者头像 李华