表级锁-间隙锁/临键锁
1、锁定索引记录间隙(不包括该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读,在RR隔离级别下支持。
例:id为索引,6-12之间的索引中添加间隙锁。
2、索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁。
举例:
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(100)
) ENGINE=InnoDB;
id是主键。
BEGIN;
SELECT * FROM user WHERE id = 3 FOR UPDATE;
如果 id = 3 存在,InnoDB 会:
- 给这条记录加“行锁”(Record Lock);
- 因为是唯一索引等值查询,只锁命中的那一行;
- 不会产生间隙锁。
不存在时的锁
如果 id = 3 不存在,那么:
- InnoDB 会加一把间隙锁(Gap Lock),锁定“id = 3 应该插入的位置”的范围;
- 假设当前表中已有数据为:id = 1, 2, 5, 7;
- id = 3 落在 (2, 5) 的间隙中;
- 那么 SELECT ... FOR UPDATE 会给 (2, 5) 加一把间隙锁,防止其他事务在这里插入 3、4 之类的值。
为什么要这样做?
这是为了防止幻读(Phantom Read):
- 如果不加锁,当前事务后续可能会再次查 id = 3,而其他事务可能插入了;
- 所以先用“间隙锁”锁住这个“空位”,保护范围内不能插入新数据;
- 即使记录本身不存在,也能防止“插入后变得存在”。
普通索引
因为普通索引不具备唯一性,因此普通索引加锁会是:行锁+间隙锁。
举例:
SELECT * FROM user WHERE name = 'Alice' FOR UPDATE;
name 是普通索引
1、命中一条记录,InnoDB加行锁 + 间隙锁。
- 行锁在 'Alice'
- 间隙锁在相邻值之间的范围,用于防止幻读(比如 'Alice' 被重复插入)
2、如果记录不存在。
SELECT * FROM user WHERE name = 'Daniel' FOR UPDATE;
加 间隙锁:锁住 'Daniel' 该在的位置,比如 'Charlie' 和 'Edward' 之间的 gap
延伸:联合索引情况也类似
例如 INDEX(name, age),如果你只使用 name='Alice' 查询,由于不满足最左前缀匹配原则,仍按普通索引规则处理。

浙公网安备 33010602011771号