能,select … for share 与 select … lock in share mode 功能完全等价,均加共享锁、事务结束时释放,但 for share 支持 of、nowait、skip locked 等增强选项,且 8.0.22 起仅需 select 权限。

FOR SHARE 能不能直接替换 LOCK IN SHARE MODE
能,且语义完全等价。只要你的 MySQL 版本 ≥ 8.0(推荐 ≥ 8.0.22),SELECT ... FOR SHARE 就是 SELECT ... LOCK IN SHARE MODE 的直系替代品,无需改逻辑、不改变锁行为、事务提交时一样释放锁。
但要注意权限变化:FOR SHARE 在 8.0.22 之前需要 SELECT + 至少一个 DELETE/LOCK TABLES/UPDATE 权限;8.0.22 起仅需 SELECT 权限——升级后若遇到权限拒绝,先检查版本再查权限配置。
多表 JOIN 场景下只锁某一张表用 OF
这是 FOR SHARE 独有、LOCK IN SHARE MODE 完全不支持的能力。当查询涉及 JOIN,你通常只希望锁定主表(比如订单表),而不锁从表(比如日志表),避免无谓的并发阻塞。
FOR SHARE OF 明确指定目标表:
SELECT o.id, o.status, l.action FROM orders o JOIN order_logs l ON o.id = l.order_id WHERE o.user_id = 123 FOR SHARE OF o;
上面这句只对 orders 表中匹配的行加共享锁,order_logs 行不加锁。若误写成 FOR SHARE OF l,则锁的是日志表——容易引发意料外的等待或死锁。
-
OF后必须是 FROM/JOIN 中已定义的表别名(如o),不能是原表名orders - 不写
OF时,默认对所有参与扫描的表加锁(和旧语法行为一致) - 多个
OF不被支持,一次FOR SHARE只能指定一个表
NOWAIT 和 SKIP LOCKED 怎么选
这两个选项解决的是“锁等待”这个高频痛点,但行为截然不同,选错会导致业务逻辑出错。
NOWAIT:请求锁时发现已被占用,立刻报错 ERROR 3572 (HY000): Statement aborted due to lock wait timeout,不等待。适合强实时性场景,比如秒杀库存预占,宁可失败也不排队。
SKIP LOCKED:跳过已被其他事务加锁的行,只返回当前可安全读取的行。典型用于任务队列消费,避免多个工作进程争抢同一条任务:
SELECT * FROM tasks WHERE status = 'pending' ORDER BY id LIMIT 1 FOR SHARE SKIP LOCKED;
注意点:
-
NOWAIT和SKIP LOCKED互斥,不能同时用 - 它们只对
FOR SHARE和FOR UPDATE生效,LOCK IN SHARE MODE不支持 -
SKIP LOCKED返回结果集可能为空(所有行都被锁),业务代码必须处理该情况
容易忽略的隐式共享锁场景
很多人以为“没写 FOR SHARE 就没共享锁”,其实 InnoDB 在某些 DML 操作中会自动加 S 锁,且不受你是否显式使用新语法影响。
典型例子:
-
INSERT INTO t VALUES (1, 'x')遇到唯一键冲突时,会对冲突行加 S 锁(防止它被删掉导致错误不可逆) -
INSERT INTO child (pid) VALUES (100)外键检查时,会对父表parent(id=100)加 S 锁 -
INSERT ... SELECT在 RR 隔离级别下,会对源表扫描到的每行加 Next-Key Lock(含 S 锁成分)
这些隐式锁无法用 OF/NOWAIT 控制,也看不到 SQL 里有 FOR SHARE。排查锁等待问题时,得结合 INFORMATION_SCHEMA.INNODB_TRX 和 INNODB_LOCK_WAITS 查真实持有者,不能只盯 SQL 写法。











