InnoDB 的行锁有哪些类型?
在 MySQL 的 InnoDB 存储引擎中,行锁(Row-level Locks)是实现并发控制和事务隔离级别的核心机制。需要特别注意的是:InnoDB 的行锁是加在“索引”上的,而不是直接加在物理行记录上。 如果查询没有命中任何索引,行锁会退化为表锁(实际上是锁住了隐藏的聚簇索引的所有记录及间隙)。
根据锁的算法和作用范围,InnoDB 的行锁主要分为以下 4 种类型:
1. 记录锁(Record Lock)
- 定义:仅仅锁住单条索引记录。
- 作用范围:精确命中一条记录。
- 触发场景:通常出现在使用唯一索引(包括主键)进行等值查询且命中记录时。
- 示例:此时,InnoDB 只会对sql
-- 假设 id 是主键 SELECT * FROM user WHERE id = 10 FOR UPDATE;id = 10的这一行索引记录加锁,阻止其他事务对其进行UPDATE或DELETE。
2. 间隙锁(Gap Lock)
- 定义:锁住索引记录之间的间隙(Gap),或者锁住第一条记录之前、最后一条记录之后的间隙。它不包含索引记录本身。
- 作用范围:开区间
(A, B)。 - 核心目的:为了阻止其他事务在这个间隙内插入(INSERT)新的数据,从而防止幻读(Phantom Read)。
- 触发场景:
- 在 可重复读(Repeatable Read, RR) 隔离级别下生效。
- 使用普通索引(非唯一索引)进行等值查询时。
- 使用范围查询(如
BETWEEN、<、>)时。
- 特性:间隙锁之间是不互斥的。事务 A 和事务 B 可以同时对同一个间隙加 Gap Lock。间隙锁唯一排斥的操作是“在间隙中插入新记录”。
3. 临键锁(Next-Key Lock)
- 定义:记录锁 + 间隙锁 的组合。它不仅锁住索引记录本身,还锁住该记录之前的那个间隙。
- 作用范围:左开右闭区间
(A, B]。 - 触发场景:Next-Key Lock 是 InnoDB 在可重复读(RR)隔离级别下的默认行锁算法。
- 示例与锁降级:
假设表中有一个普通索引包含值:10, 20, 30。
Next-Key Lock 可以划分的区间为:(-∞, 10],(10, 20],(20, 30],(30, +∞)。- 当执行
SELECT * FROM table WHERE age = 20 FOR UPDATE;时,会锁住(10, 20]区间,同时为了防止在 20 后面插入,还会加上(20, 30)的间隙锁。 - 锁降级机制:
- 当查询的索引是唯一索引(或主键),且属于等值查询并命中记录时,Next-Key Lock 会降级为记录锁(Record Lock)。
- 当等值查询未命中记录时,Next-Key Lock 会降级为间隙锁(Gap Lock)。
- 当执行
4. 插入意向锁(Insert Intention Lock)
- 定义:这是一种特殊的间隙锁。由
INSERT操作在插入数据之前产生。 - 核心目的:提高插入并发度。它表示一种插入的意向,即“我打算在这个间隙里插入一条数据”。
- 特性:
- 互不排斥:如果多个事务向同一个间隙插入不同的数据,它们都会获取这个间隙的插入意向锁,但彼此之间不会阻塞。例如间隙是
(10, 20),事务 A 插入 15,事务 B 插入 16,两者都能顺利执行。 - 被 Gap/Next-Key Lock 排斥:如果事务 A 对间隙
(10, 20)加了普通的间隙锁或临键锁,那么事务 B 想要在这区间插入数据时获取“插入意向锁”就会被阻塞。
- 互不排斥:如果多个事务向同一个间隙插入不同的数据,它们都会获取这个间隙的插入意向锁,但彼此之间不会阻塞。例如间隙是
补充:行锁的模式(Lock Modes)
以上 4 种类型是基于“锁的范围/算法”划分的。从“读写排他性”来看,行锁又分为两种模式:
- 共享锁(Shared Lock,简称 S 锁 / 读锁)
- 允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
- 触发语句:
SELECT ... LOCK IN SHARE MODE(MySQL 8.0 推荐FOR SHARE)。
- 排他锁(Exclusive Lock,简称 X 锁 / 写锁)
- 允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享锁和排他锁。
- 触发语句:
SELECT ... FOR UPDATE、UPDATE、DELETE。
总结
InnoDB 通过 Record Lock 保障数据行不被并发修改,通过 Gap Lock 和 Next-Key Lock 解决可重复读级别下的“幻读”问题,并通过 Insert Intention Lock 保证了在防止幻读的同时兼顾了数据插入的并发性能。