【大白话说Java面试题 第86题】【Mysql篇】第16题:MySQL 中锁的种类与行锁实现原理?

📌 PDF :大白话说Java面试题 --- 03-Mysql篇

第16题:MySQL 中锁的种类与行锁实现原理

📚 回答:

  • 核心考点
    大厂面试要求深入理解锁的分类体系InnoDB行锁的底层实现各种锁的加锁时机与兼容性 ,以及行锁与隔离级别的关联。面试官常追问:"Record Lock、Gap Lock、Next-Key Lock有什么区别?"、"意向锁的作用是什么?"、"行锁是怎么在索引上实现的?"
1. 锁的完整分类体系

MySQL的锁可以从三个维度分类:加锁思想锁粒度锁类型

分类维度 类型 说明
加锁思想 乐观锁 / 悲观锁 逻辑上的并发控制策略,非数据库内置锁
锁粒度 表锁 / 页锁 / 行锁 锁的范围大小
锁类型(InnoDB行锁) Record Lock / Gap Lock / Next-Key Lock / Insert Intention Lock InnoDB行锁的具体实现
表级辅助锁 意向锁(IS/IX) 用于表锁与行锁的协调
2. 乐观锁 vs 悲观锁(并发控制思想)
对比 乐观锁 悲观锁
核心思想 假设冲突少,先操作后检测 假设冲突多,先加锁后操作
实现方式 版本号(version)、CAS SELECT FOR UPDATELOCK IN SHARE MODE
是否数据库内置 否(应用层实现) 否(依赖于数据库提供的锁)
适用场景 读多写少 写多读少

乐观锁示例

sql 复制代码
-- 使用version字段实现乐观锁
UPDATE products 
SET stock = stock - 1, version = version + 1 
WHERE id = 1 AND version = 5;
-- 检查affected_rows,为0则重试

悲观锁示例

sql 复制代码
START TRANSACTION;
SELECT * FROM products WHERE id = 1 FOR UPDATE; -- 加排他锁
UPDATE products SET stock = stock - 1 WHERE id = 1;
COMMIT;
3. 按锁粒度分类
锁粒度 支持引擎 特点 性能
表锁 MyISAM、InnoDB(DDL/意向锁) 锁定整表,简单但并发差
页锁 BDB(已废弃) 锁定数据页,介于表锁和行锁之间
行锁 InnoDB 锁定索引记录,并发高

InnoDB表锁场景

  • DDL操作(如ALTER TABLE)会加表锁
  • 事务执行LOCK TABLES ... WRITE显式加表锁
  • 意向锁(IS/IX)是表级锁,用于快速判断表锁兼容性
4. InnoDB 行锁的四种类型(核心)

4.1 记录锁(Record Lock)

  • 定义 :锁定索引记录(不是行本身),防止其他事务修改或删除该记录
  • 加锁对象:索引项(没有索引时自动使用隐藏聚簇索引)
  • SQL示例
sql 复制代码
SELECT * FROM users WHERE id = 1 FOR UPDATE; -- 锁定id=1的索引记录

4.2 间隙锁(Gap Lock)

  • 定义 :锁定索引记录之间的间隙(开区间),不包括记录本身
  • 作用 :防止其他事务在间隙中插入新记录,避免幻读
  • 生效条件:RR隔离级别及以上
  • SQL示例
sql 复制代码
-- 假设id值:1, 3, 5, 7, 9
SELECT * FROM users WHERE id BETWEEN 3 AND 5 FOR UPDATE;
-- 锁定间隙:(1,3)、(3,5)、(5,7)

4.3 Next-Key Lock

  • 定义Record Lock + Gap Lock ,锁定一个左开右闭的区间
  • 计算公式:Next-Key Lock = (前一个值, 当前值] 的间隙锁 + 当前值的记录锁
  • 作用 :InnoDB在RR级别下的默认行锁算法,同时防止幻读和保证当前读一致性
  • SQL示例
sql 复制代码
-- 假设id值:1, 3, 5, 7, 9
SELECT * FROM users WHERE id = 5 FOR UPDATE;
-- Next-Key Lock锁定范围:(3,5] 和 (5,7]
-- 即锁住id=5的记录,以及(3,5)和(5,7)的间隙

4.4 插入意向锁(Insert Intention Lock)

  • 定义 :一种特殊的间隙锁,表示事务意图在某个间隙中插入数据
  • 特性 :多个事务可以同时在同一间隙中持有插入意向锁,只要插入的位置不冲突(不同索引值)
  • 作用:提高插入并发性,避免间隙锁完全阻塞所有插入
sql 复制代码
-- 事务A锁定间隙(3,5)
SELECT * FROM users WHERE id = 4 FOR UPDATE; -- 加间隙锁(3,5)

-- 事务B在间隙(3,5)中插入id=4.5
INSERT INTO users VALUES (4.5, 'xxx'); -- 阻塞(与间隙锁冲突)

-- 事务C在间隙(3,5)中插入id=4.8
INSERT INTO users VALUES (4.8, 'xxx'); -- 也阻塞(间隙锁阻塞所有插入)
5. 行锁的加锁规则与退化

唯一索引等值查询

条件 加锁类型 示例(id主键:1,3,5,7,9)
命中 Record Lock WHERE id=5 → 锁5(Next-Key锁退化)
未命中 Gap Lock WHERE id=4 → 锁(3,5)间隙

普通索引等值查询

条件 加锁类型 示例(age普通索引:1,3,5,7,9)
命中 Next-Key Lock WHERE age=5 → 锁(3,5]和(5,7)
未命中 Gap Lock WHERE age=4 → 锁(3,5)间隙

范围查询

sql 复制代码
-- 唯一索引范围
SELECT * FROM users WHERE id > 5 FOR UPDATE;
-- 锁:所有id>5的Record Lock + 后续Gap Lock到无穷大

-- 普通索引范围
SELECT * FROM users WHERE age > 5 FOR UPDATE;
-- 锁:所有age>5的Next-Key Lock
6. 意向锁(Intention Lock)详解

6.1 什么是意向锁

意向锁是表级锁 ,表示事务意图对表中的某些行加锁。

锁类型 含义 加锁时机
意向共享锁(IS) 事务意图对某些行加共享锁(S锁) SELECT ... LOCK IN SHARE MODE
意向排他锁(IX) 事务意图对某些行加排他锁(X锁) SELECT ... FOR UPDATEUPDATEDELETE

6.2 意向锁的作用

避免表锁与行锁的冲突检测遍历

  • 事务A要对整表加LOCK TABLES ... WRITE(表级X锁)
  • 如果没有意向锁,需要遍历所有行检查是否有行锁 → O(n)复杂度
  • 有意向锁时,只需检查表上的意向锁 → O(1)复杂度

6.3 意向锁兼容性矩阵

锁类型 IS IX S(表级) X(表级)
IS
IX
S(表级)
X(表级)

关键规律

  • 意向锁之间相互兼容(IS与IS、IS与IX、IX与IX都兼容)
  • 意向锁与表级共享锁(S)部分兼容(IS兼容,IX不兼容)
  • 意向锁与表级排他锁(X)不兼容
7. 行锁与隔离级别的关系
隔离级别 是否使用Gap Lock 幻读风险 说明
READ UNCOMMITTED 几乎不加锁
READ COMMITTED 只有Record Lock,无Gap Lock
REPEATABLE READ InnoDB默认,使用Next-Key Lock
SERIALIZABLE 所有SELECT隐式加锁

RC vs RR的行锁差异

sql 复制代码
-- RC级别:只有Record Lock
-- RR级别:Next-Key Lock(Record Lock + Gap Lock)

-- 示例:假设id值:1, 3, 5, 7, 9
SELECT * FROM users WHERE id = 5 FOR UPDATE;

-- RC级别:只锁id=5的记录(其他事务可插入4、6)
-- RR级别:锁(3,5]和(5,7)(其他事务不可插入4、6)
8. 行锁的实现原理(源码层面)

8.1 行锁加在索引上

InnoDB的行锁本质是索引项锁

  • 通过主键索引查询 → 锁聚簇索引记录
  • 通过二级索引查询 → 先锁二级索引记录,再锁聚簇索引记录

为什么必须是索引?

  • 行锁通过索引定位记录
  • 没有索引条件时,InnoDB会锁全表(所有聚簇索引记录)

8.2 两阶段锁协议(2PL)

  • 加锁阶段:事务开始到提交前,可以随时加锁
  • 解锁阶段:事务提交或回滚时,统一释放所有锁
  • 无中途解锁 :InnoDB不支持UNLOCK操作

8.3 锁的内存结构

每个锁在内存中对应一个lock_t结构,包含:

  • trx_id:持有锁的事务ID
  • type_mode:锁类型(S/X/IS/IX/GAP等)
  • index:锁定的索引
  • bitmap:行记录位图(一个锁可锁定多条记录)
9. 死锁检测与处理

9.1 死锁产生条件

四个必要条件:互斥、持有并等待、不可剥夺、循环等待。

9.2 典型死锁场景

sql 复制代码
-- 事务A
START TRANSACTION;
UPDATE users SET name='A' WHERE id=1; -- 锁id=1
UPDATE users SET name='A' WHERE id=2; -- 等待事务B释放id=2

-- 事务B
START TRANSACTION;
UPDATE users SET name='B' WHERE id=2; -- 锁id=2
UPDATE users SET name='B' WHERE id=1; -- 等待事务A释放id=1
-- 死锁形成

9.3 InnoDB死锁处理机制

  • 死锁检测 :维护事务等待图(trx_tlock_t),检测到循环等待时立即处理
  • 死锁回滚 :回滚代价较小的事务(修改行数少的)
  • 参数控制
    • innodb_deadlock_detect=ON(默认开启)
    • innodb_lock_wait_timeout=50(死锁检测超时,默认50秒)

查看死锁信息

sql 复制代码
SHOW ENGINE INNODB STATUS;  -- 查看最近死锁
SELECT * FROM information_schema.INNODB_TRX;  -- 查看当前事务
SELECT * FROM information_schema.INNODB_LOCKS;  -- 查看当前锁
10. 总结对比表
锁类型 粒度 作用 是否阻塞插入 RR级别默认
Record Lock 行(索引记录) 锁定单条记录
Gap Lock 间隙 锁定索引间隙,防幻读
Next-Key Lock 行+间隙 锁定记录及前间隙 ✅(默认)
Insert Intention Lock 间隙 表示意图插入 特殊(位置不冲突时兼容)
意向锁(IS/IX) 协调表锁与行锁

💡 面试官想要的满分总结

"InnoDB的锁体系从三个维度理解:
一、按加锁思想:乐观锁(版本号/CAS,应用层实现)和悲观锁(数据库行锁/表锁)。
二、按锁粒度:表锁(MyISAM/DDL)、行锁(InnoDB核心)、页锁(已废弃BDB)。
三、InnoDB行锁四种类型

  • Record Lock:锁定索引记录(行锁的基础)

  • Gap Lock:锁定索引间隙,防幻读(RR级别生效)

  • Next-Key Lock:Record Lock + Gap Lock,RR级别默认算法

  • Insert Intention Lock:特殊的间隙锁,提高插入并发性
    **意向锁(IS/IX)**是表级锁,用于快速判断表锁兼容性,避免遍历所有行检查行锁。
    实现原理

  • 行锁实质是索引项锁,通过索引定位记录

  • 遵循两阶段锁协议(2PL):事务结束时统一释放锁

  • 死锁检测通过等待图实现,回滚代价较小的事务
    一句话:InnoDB行锁 = 基于索引的记录锁 + RR级别下的间隙锁(防幻读),通过意向锁快速协调表锁与行锁,通过死锁检测自动处理循环等待。"


觉得对您有帮助,麻烦 点点关注啦 ,您的关注是我创作的最大动力~ 🎯

相关推荐
不爱编程的小陈1 小时前
Go内存模型与GC机制:高性能编程的核心
开发语言·后端·golang
染指11101 小时前
14.LangChain框架5-文档切分
数据库·人工智能·ai·langchain
abcy0712131 小时前
【无标题】
数据库·sqlite
右耳朵猫AI1 小时前
PHP技术周刊 2026年第20周
开发语言·php
code2roc1 小时前
SpringBoot整合Milvus向量数据库
数据库·spring boot·milvus·向量化
日月云棠1 小时前
12 Enum —— 枚举类型的底层实现
java·后端
AugustRed1 小时前
Flyway 数据库版本迁移 零基础完整学习文档
数据库·学习
轻刀快马1 小时前
从繁琐到极简,从幻象到本质:Spring AOP 架构演进与实战避坑指南
java·spring·架构
weixin_BYSJ19871 小时前
springboot旅游管理系统04470(附源码+开发文档+部署教程)
java·spring boot·python·算法·django·flask·旅游