for update 加排他锁(x 锁),用于先查后改场景,必须在 autocommit=0 的显式事务中使用,且依赖索引避免表锁;lock in share mode 加共享锁(s 锁),适用于读多写少的校验场景,允许多个并发读但阻塞写操作。

FOR UPDATE 和 LOCK IN SHARE MODE 的核心区别在于锁类型和并发行为:前者加排他锁(X 锁),后者加共享锁(S 锁);同一行上不能同时存在 X 锁和 S 锁,但可并存多个 S 锁。
FOR UPDATE 是什么锁?什么时候必须用它?
它在查询结果行上加的是排他锁(X 锁),意味着其他事务既不能修改这些行,也不能执行 SELECT ... FOR UPDATE 或 SELECT ... LOCK IN SHARE MODE,甚至连某些隔离级别下的当前读都会被阻塞。但它不阻塞普通 SELECT(快照读)。
- 典型场景:先查后改,比如扣库存、生成订单前校验余额
- 必须在显式事务中使用:
BEGIN/START TRANSACTION后才生效 - 如果
autocommit = 1,SELECT ... FOR UPDATE执行完立刻释放锁,等于没锁——务必确认SET autocommit = 0 - 加锁范围取决于查询条件是否命中索引:未走索引会升级为表锁,极危险
LOCK IN SHARE MODE 是什么锁?适合哪些读多写少的场景?
它加的是共享锁(S 锁),允许其他事务并发读(包括再次加 S 锁),但会阻止任何事务对该行加 X 锁或执行 UPDATE/DELETE,直到本事务提交或回滚。
- 典型场景:父子表约束校验(如插入子记录前锁定父记录)、只读校验后需保证父数据不被删
- 不能用于“先查后改”逻辑:两个事务同时
LOCK IN SHARE MODE后再UPDATE,大概率触发死锁 - 同样依赖索引:全表扫描时可能锁住整张表,性能雪崩
- 注意它不阻止快照读,所以业务上仍需结合事务隔离级别判断一致性
两者加锁范围差异:为什么 FOR UPDATE 有时比 LOCK IN SHARE MODE 多锁一行?
当查询需要回表(例如 SELECT * FROM t WHERE c1 = 3,且 c1 是二级索引),FOR UPDATE 会在二级索引记录 + 对应主键记录上都加 X 锁;而 LOCK IN SHARE MODE 同样加 S 锁在这两处——此时锁数量一致。
但若只查索引字段(SELECT c1 FROM t WHERE c1 = 3),FOR UPDATE 仍会额外在主键上加 X 锁(因 InnoDB 内部机制),而 LOCK IN SHARE MODE 通常只锁二级索引记录本身。这意味着:
- 前者锁粒度更重,阻塞面更广
- 后者在纯校验类场景下资源开销略小
- 实际锁行为还受事务隔离级别(如 RR vs RC)和索引结构影响,不能仅凭语句字面推断
容易踩的坑:锁失效、死锁、幻读都从这里来
最常被忽略的是事务边界和索引依赖:
-
SELECT ... FOR UPDATE在autocommit=1下锁秒放,后续UPDATE实际无保护 - 没走索引的
WHERE条件会让行锁退化为表锁,尤其在大表上极易拖垮整个库 - 用
LOCK IN SHARE MODE做“查+改”流程,两个事务并发执行会导致互相等待对方释放 S 锁才能升级为 X 锁,最终超时回滚 - 间隙锁(Gap Lock)不在这两种语句的默认行为里,但若在可重复读(RR)隔离级别下做范围查询(如
WHERE id > 10),它们会隐式带上间隙锁——这点文档极少明说,却常引发意料外的阻塞
锁不是万能胶,它解决的是特定并发冲突;选错类型或漏掉前提条件,反而让系统更脆弱。











