MySQL 中如何加共享锁(S锁 Share Lock)
一、什么是共享锁(S锁)?
-
共享锁(Share Lock,S锁) 是一种读锁,允许多个事务同时读取同一份数据,但在加锁期间禁止任何事务修改(UPDATE/DELETE)这份数据。
-
特点:
-
多个事务可以同时对同一数据加 S 锁,共享读取;
-
但在加了 S 锁的数据上,不能加 X 锁(排他锁,修改、删除);
-
遇到修改冲突时,会阻塞直到前面的 S 锁释放。
-
二、如何加共享锁?(标准写法)
✅ 通过 SELECT ... LOCK IN SHARE MODE
START TRANSACTION; -- 显式开启事务
SELECT * FROM 表名 WHERE 条件 LOCK IN SHARE MODE;
-- 后续可以基于查询结果进行其他操作
COMMIT; -- 提交事务
示例:
START TRANSACTION;
SELECT * FROM user WHERE id = 10 LOCK IN SHARE MODE;
COMMIT;
解析:
-
查询
id=10
的user
表记录时,加上了共享锁。 -
其他事务可以继续读取
id=10
的记录,但在当前事务提交前,不能修改或删除这一行,否则会被阻塞。
三、应用场景
应用场景类型 | 示例 | 原因 |
---|---|---|
1. 读后校验,不修改 | 比如读取某用户订单状态,确保未被其他事务更改,防止并发异常。 | 共享锁能保护读到的数据在校验完成前不会被修改。 |
2. 防止幻读(搭配显式加锁) | 有些自定义事务控制的业务,比如乐观检查库存数量。 | 保证读取到的数据在提交前是可靠的。 |
四、注意事项
1. 必须在事务中使用
-
如果你没有用
START TRANSACTION
显式开启事务,MySQL默认是自动提交模式,SELECT ... LOCK IN SHARE MODE
结束后锁立即释放,等于没意义。
所以正确姿势:
START TRANSACTION;
SELECT ... LOCK IN SHARE MODE;
-- 进行一些基于读取数据的逻辑判断
-- 或根据查询结果进一步处理
COMMIT;
2. 共享锁与排他锁互斥
-
如果某行被加了共享锁,其他事务想对这行加排他锁(比如更新或删除),必须等待共享锁释放。
示例:
-
事务A
START TRANSACTION; SELECT * FROM user WHERE id=1 LOCK IN SHARE MODE;
-
事务B
START TRANSACTION; UPDATE user SET name='new' WHERE id=1; -- 被阻塞
3. 对主键/唯一索引字段加共享锁最有效
-
加锁基于索引,主键或唯一索引查询加锁范围小、效率高。
-
如果查询列无索引,InnoDB可能锁全表扫描,极大降低并发性。
✅ 建议加锁操作要走索引!
五、共享锁和其他锁关系简表
锁类型 | 允许并发读? | 允许并发写? | 典型用途 |
---|---|---|---|
无锁(普通 SELECT) | ✅ | ✅ | 快速读取 |
共享锁(S锁) | ✅ | ❌ | 读取保护,防止别人改 |
排他锁(X锁) | ❌ | ❌ | 读写独占,比如 FOR UPDATE |
最后一段总结
✅ 加共享锁的标准方式是 SELECT ... LOCK IN SHARE MODE
,必须在事务中使用,允许多个事务同时读但禁止写,适合在高并发业务中需要保护读取数据一致性的场景。