外观
where id > 10 会加锁吗?什么锁?id > 20 的记录会不会被锁
⭐ 题目日期:
字节 - 2024/12/17
📝 题解:
在 MySQL 的 可重复读(Repeatable Read) 隔离级别下,执行 WHERE id > 10
的查询时,是否会加锁以及具体锁的范围,取决于查询是否显式加锁。以下是详细分析:
1. 不加锁的普通查询
如果只是普通查询(如 SELECT * FROM table WHERE id > 10
),不会加任何锁。此时:
- 其他事务可以自由插入、修改或删除
id > 10
的记录。 - 幻读可能发生:若其他事务插入
id > 10
的新记录,当前事务再次查询时会出现幻读。
2. 显式加锁的查询
如果通过 FOR UPDATE
或 LOCK IN SHARE MODE
显式加锁(如 SELECT * FROM table WHERE id > 10 FOR UPDATE
),InnoDB 会通过 Next-Key Locks 锁定范围。具体锁行为如下:
2.1 加的锁类型
- Next-Key Lock:行锁(Record Lock) + 间隙锁(Gap Lock)。
- 锁范围:锁定所有满足
id > 10
的 现有记录 和 间隙(包括未来可能插入的id > 10
的记录)。
2.2 锁的覆盖范围
假设表中现有记录为 id = 5, 15, 25
,则:
- 锁定记录:
id = 15, 25
(行锁)。 - 锁定间隙:
(5, 15)
:现有记录之间的间隙。(15, 25)
:现有记录之间的间隙。(25, +∞)
:最大记录后的间隙。
- 结论:所有
id > 10
的记录(如id = 15, 25
)及间隙均被锁定,id > 20
的记录(如25
)也会被锁定。
3. 示例验证
事务 A(加锁方)
BEGIN;
-- 显式加锁,锁定 id > 10 的范围
SELECT * FROM users WHERE id > 10 FOR UPDATE;
事务 B(尝试操作)
- 插入
id = 12
:被阻塞(间隙(10, 15)
被锁定)。 - 插入
id = 30
:被阻塞(间隙(25, +∞)
被锁定)。 - 修改
id = 15
:被阻塞(行锁)。 - 结论:所有
id > 10
的操作均被阻塞,直到事务 A 提交。
4. 锁的底层原理
Next-Key Lock 的作用
- 行锁(Record Lock):锁定已存在的记录(如
id = 15, 25
)。 - 间隙锁(Gap Lock):锁定索引范围之间的间隙(如
(5, 15)
、(15, 25)
、(25, +∞)
)。 - 组合效果:禁止其他事务在锁定范围内插入或修改数据。
锁的兼容性
- 排他锁(X Lock):
FOR UPDATE
加的锁是排他锁,完全禁止其他事务读写。 - 共享锁(S Lock):
LOCK IN SHARE MODE
加的锁是共享锁,允许其他事务读但禁止写。
5. 关键结论
问题 | 答案 |
---|---|
WHERE id > 10 是否会加锁? | 普通查询不加锁;显式加锁(如 FOR UPDATE )会加 Next-Key Lock。 |
加的什么锁? | Next-Key Lock(行锁 + 间隙锁)。 |
id > 20 的记录是否被锁? | 是,所有 id > 10 的现有记录及间隙均被锁定,包括 id > 20 的记录。 |
6. 注意事项
- 索引依赖:
- Next-Key Lock 依赖索引,若
id
无索引,会退化为 表锁,严重影响性能。
- Next-Key Lock 依赖索引,若
- 死锁风险:
- 多个事务按不同顺序加锁可能导致死锁(如事务 A 锁定
id > 10
,事务 B 锁定id > 20
)。
- 多个事务按不同顺序加锁可能导致死锁(如事务 A 锁定
- 性能权衡:
- 范围锁会降低并发性能,需根据业务需求决定是否显式加锁。
总结:
WHERE id > 10
的显式加锁查询会锁定所有id > 10
的现有记录及间隙,包括id > 20
的记录。- 通过 Next-Key Lock 机制,MySQL 在可重复读隔离级别下有效防止幻读,但需谨慎使用以避免性能问题。