S锁与X锁兼容性规则为:S与S兼容、S与X互斥、X与X互斥;同一行上多事务可并存S锁以支持并发读,但任一S或X锁存在时,对方X锁请求均被阻塞,且S/X共存会破坏隔离性导致脏读或更新丢失。

S锁与X锁的兼容性规则非常直接:S与S兼容,S与X互斥,X与X互斥。 这不是理论推演,而是InnoDB引擎底层锁队列调度的真实行为——只要有一方持X锁,另一方无论想加S还是X都会被挂起等待;而多个S锁可以并存,这是并发读的基石。
为什么S锁之间能同时存在
多个事务对同一行执行 SELECT ... LOCK IN SHARE MODE,各自获得S锁,不会阻塞。因为读操作不修改数据,无需互斥。但要注意:这个“兼容”仅针对同一行记录;如果事务A锁id=1、事务B锁id=2,那是两把独立的S锁,根本不在同一个锁冲突域里,谈不上兼容与否。
- 同一个事务内重复执行
SELECT ... LOCK IN SHARE MODE不会触发兼容性检查——InnoDB知道是自己持有的锁,直接复用 - 跨事务时,S锁申请只被X锁阻塞,不会被其他S锁阻塞
- 一旦某行已有X锁(比如被
SELECT ... FOR UPDATE或UPDATE占用),任何新S锁请求都会进入等待队列
为什么S锁和X锁绝对不能共存
允许S和X共存就等于允许“一边读一边写”,这会直接破坏隔离性。典型场景:事务A用 SELECT ... LOCK IN SHARE MODE 加了S锁读某行,事务B却用 UPDATE 加X锁改同一行——A可能读到半更新状态或更新丢失,MVCC也救不了。
- InnoDB在加锁前会检查锁兼容矩阵,发现S+X组合为
Conflict,立刻拒绝X锁请求(或让其等待) - 错误日志里通常不会明说“S/X冲突”,只会显示类似
Waiting for table level lock或Lock wait timeout exceeded,需要结合INFORMATION_SCHEMA.INNODB_TRX和INNODB_LOCK_WAITS手动关联分析 - 注意:这里说的“不能共存”指同一行记录上;不同行、不同表之间完全无关
意向锁IS/IX是怎么悄悄拦路的
你看到的“S/X冲突”,90%其实不是行锁直接打架,而是背后意向锁在起作用。比如事务A执行 SELECT ... FOR UPDATE,先在表上加 IX 锁;此时另一个事务想对整张表加 LOCK TABLES t READ(即表级S锁),就会因 IX 与表级S锁冲突而失败——但报错信息里根本不提IX。
-
IS和IX本身相互兼容,但IX与任何表级S/X锁都冲突 - 所有行级S/X操作都必须先获取对应意向锁:加S锁 → 先加
IS;加X锁 → 先加IX - 排查锁等待时,务必查
INNODB_METRICS中的lock_row_lock_waits和lock_table_lock_waits,区分是行锁争用还是表锁意图被拦
真正容易被忽略的点在于:锁冲突常发生在“看不见”的意向层,而不是你写的那条 SELECT ... FOR UPDATE 语句本身。看锁,不能只盯SQL,得顺着 trx_id → lock_trx_id → lock_mode 一层层往下挖。


















