共享锁(s锁)与排他锁(x锁)核心区别在于:s锁之间兼容可共存,s锁与x锁互斥,x锁之间也互斥;即lock in share mode允许多事务并发读,而for update会阻塞其他事务对同一行的任何加锁操作(含s锁和x锁),确保读写原子性。

共享锁(S锁)和排他锁(X锁)的兼容性表现
核心区别就一条:共享锁之间能共存,但共享锁与排他锁互斥,排他锁之间也互斥。这意味着 SELECT ... LOCK IN SHARE MODE 可以被多个事务同时执行,而 SELECT ... FOR UPDATE 一旦被一个事务持有,其他任何事务再尝试对同一行加 S 锁或 X 锁都会阻塞,直到前一个事务释放锁。
常见错误现象是:业务里用了 SELECT ... LOCK IN SHARE MODE 做“读校验”,接着在应用层判断是否允许更新,再发 UPDATE —— 这中间存在时间窗口,其他事务可能已用 FOR UPDATE 锁住该行,导致后续 UPDATE 等待甚至超时。这不是锁机制错了,而是逻辑上漏掉了“读-写”原子性。
什么时候该用 LOCK IN SHARE MODE,什么时候必须用 FOR UPDATE
用 LOCK IN SHARE MODE 的典型场景是:需要确保读到的数据在本事务结束前不被其他事务修改(比如校验库存是否充足),但自己并不打算改它;而 FOR UPDATE 必须用于“先读后改”的闭环操作,例如扣减余额、生成订单号、状态机流转等。
- 如果只读不写,又怕别人改,用
LOCK IN SHARE MODE - 如果读完马上要
UPDATE或DELETE,直接用FOR UPDATE,避免二次加锁开销和竞态 -
FOR UPDATE在唯一索引 + 等值查询下只锁匹配行;若走全表扫描或范围查询,可能升级为间隙锁或临键锁,影响范围远超预期
SELECT ... FOR UPDATE 不生效的常见原因
很多人发现加了 FOR UPDATE 却没锁住行,最常踩的坑是事务隔离级别和索引缺失:
- 在
READ COMMITTED隔离级别下,UPDATE/DELETE语句只在执行时加锁,语句结束即释放;但SELECT ... FOR UPDATE的锁会持续到事务结束 —— 所以务必确认当前会话的tx_isolation值 - 没走索引的
FOR UPDATE查询会退化为表级锁(InnoDB 下实际是锁全聚簇索引),不仅性能差,还容易引发大面积阻塞 - 查询条件中用了函数、隐式类型转换、或
OR拆分导致索引失效,同样会让锁粒度失控
意向锁(IS/IX)不是可选配置,而是你感知不到却必须理解的底层机制
你从不手动加 IS 或 IX 锁,但每次执行 LOCK IN SHARE MODE 或 FOR UPDATE,InnoDB 都会自动在表级别加上对应意向锁。它的作用不是锁数据,而是告诉其他想加表锁的事务:“这表里有行正被我锁着”。
所以当你看到 ALTER TABLE 或 DROP TABLE 被卡住,或者 LOCK TABLES ... WRITE 一直等待,大概率是某条长事务的 FOR UPDATE 还没提交,它持有的 IX 锁阻止了表级写锁获取 —— 此时查 information_schema.INNODB_TRX 和 INNODB_LOCK_WAITS 才能定位源头。











