共享锁(S锁)的加锁场景
一、显式加锁(主动请求)
1. SELECT ... FOR SHARE(最常用)
-- 事务A
BEGIN;
SELECT * FROM products WHERE id = 10 FOR SHARE;
-- 对 id=10 这条记录加 S 锁
-- 自己可以读,别人可以读,但谁都不能改
适用场景:
-
需要确保读取的数据在事务期间不被修改
-
允许其他事务并发读取
-
典型应用:报表生成、数据核对
2. LOCK IN SHARE MODE(MySQL 8.0 之前的写法)
-- MySQL 5.7 及更早版本
SELECT * FROM products WHERE id = 10 LOCK IN SHARE MODE;
-- 8.0 中仍兼容,但推荐用 FOR SHARE
二、隐式加锁(自动加)
1. 唯一键冲突检测(我们深入讨论过的场景)
-- 表中已存在 id=25
事务A: INSERT INTO products (id, name) VALUES (25, '新品');
-- 发现 id=25 已存在,对这条记录加 S 锁
-- 目的:防止在检测冲突到返回错误的窗口期内,这条记录被别人删掉
为什么加 S 锁:确保冲突检测的准确性。如果不加锁,可能出现:
-
事务A检测到 id=25 存在(准备报错)
-
事务B删除 id=25
-
事务A返回"Duplicate entry",但记录已经不在了 → 数据不一致
2. 外键约束检查
-- 父表 parent (id PRIMARY KEY)
-- 子表 child (pid FOREIGN KEY REFERENCES parent(id))
事务A: INSERT INTO child (id, pid) VALUES (1, 100);
-- 需要检查父表是否存在 id=100
-- 对父表 id=100 的记录加 S 锁
为什么加 S 锁:防止在检查过程中,父表的这条记录被删除或修改,导致外键约束失效。
3. INSERT ... SELECT 中的源表
-- RR 隔离级别下
INSERT INTO t1 SELECT * FROM t2 WHERE price > 100;
-- 对 t2 中扫描到的记录加 S 锁(实际上是 Next-Key Lock,包含 S 锁)
为什么加 S 锁:确保源表数据在复制过程中不被修改,保证数据一致性。
4. UPDATE 中的读取操作
UPDATE products SET price = price * 1.1
WHERE category_id = 5 AND price > 100;
-- 在找到要更新的记录时,需要先读取判断条件
-- 这些读取操作会加 S 锁(然后立即升级为 X 锁)
注意:这不是独立的 S 锁阶段,而是 UPDATE 内部流程的一部分。
一、为什么不直接加X锁?
三、特殊隔离级别下的隐式加锁
1. SERIALIZABLE 隔离级别
-- 设置隔离级别为 SERIALIZABLE
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
SELECT * FROM products WHERE price = 30;
-- 普通 SELECT 也会加 S 锁(Next-Key Lock)
为什么:SERIALIZABLE 要求所有读都是加锁读,防止任何并发修改。
四、不同场景的 S 锁持续时间
| 场景 | S 锁持有时间 | 说明 |
|---|---|---|
SELECT ... FOR SHARE |
直到事务结束(COMMIT/ROLLBACK) | 显式加锁,必须手动结束事务 |
| 唯一键冲突的 INSERT | 直到事务结束 | 即使报错,S 锁还留着 |
| 外键约束检查 | 通常很快释放 | 检查完成后可释放(取决于隔离级别) |
INSERT ... SELECT 源表 |
直到 SELECT 完成 | RR 下持续到语句结束 |
| SERIALIZABLE 的 SELECT | 直到事务结束 | 和 FOR SHARE 类似 |
五、总结:什么时候用 S 锁?
| 场景分类 | 具体场景 | S 锁的作用 |
|---|---|---|
| 显式保护 | FOR SHARE |
确保读取的数据不被修改 |
| 冲突检测 | 唯一键冲突 | 防止冲突记录被删除 |
| 约束维护 | 外键检查 | 确保父记录存在且不变 |
| 数据复制 | INSERT...SELECT | 保证源数据一致性 |
| 隔离级别 | SERIALIZABLE | 强制所有读加锁 |
一句话总结
S 锁用在"我要读,但不改,同时确保别人也不能改"的所有场景——无论是主动请求(FOR SHARE),还是被动保护(唯一键冲突、外键检查)。

浙公网安备 33010602011771号