SELECT ... LOCK IN SHARE MODE只阻塞SELECT ... FOR UPDATE、UPDATE和DELETE,不阻塞普通SELECT、自身共享锁及INSERT(但在REPEATABLE READ下范围查询可能因间隙锁阻塞INSERT);其“不生效”主因是autocommit=1或主从复制binlog格式非ROW。

SELECT ... LOCK IN SHARE MODE 会阻塞哪些语句?
它只阻塞其他事务对同一行执行 SELECT ... FOR UPDATE、UPDATE 或 DELETE,但不会阻塞普通 SELECT,也不会阻塞另一个事务的 SELECT ... LOCK IN SHARE MODE。
常见误解是“共享锁能防并发修改”——错。两个事务同时执行 SELECT ... LOCK IN SHARE MODE 都能成功加锁,接着各自 UPDATE 就会互相等待,甚至触发死锁。这不是锁没起作用,而是共享锁本来就不排斥别的共享锁。
- 阻塞:
SELECT ... FOR UPDATE、UPDATE WHERE id = ?、DELETE FROM t WHERE id = ? - 不阻塞:
SELECT *(快照读)、SELECT ... LOCK IN SHARE MODE(可重入)、INSERT(但注意:在 REPEATABLE READ 下可能因间隙锁被阻塞)
为什么 INSERT 有时会被 LOCK IN SHARE MODE 阻塞?
关键在隔离级别和锁类型:在 REPEATABLE READ 下,SELECT ... LOCK IN SHARE MODE 若走非唯一索引或范围条件(如 WHERE age > 15),InnoDB 会加 Next-Key Lock(记录锁 + 间隙锁),而 INSERT 需要先获取 insert intention lock,该锁与间隙锁冲突,就会等待超时。
实测中,SELECT * FROM next_key WHERE age > 15 LOCK IN SHARE MODE 后,INSERT INTO next_key(name,age) VALUES('Lili',40) 直接报 ERROR 1205 (HY000): Lock wait timeout exceeded,就是因为间隙被锁住。
- 只在
REPEATABLE READ下出现;READ COMMITTED下无间隙锁,INSERT不会被阻塞 - 若查询走唯一索引且等值匹配(如
WHERE id = 3),则只加记录锁,INSERT不受影响 - 检查方式:用
SELECT * FROM information_schema.INNODB_TRX看trx_state = 'LOCK WAIT'和trx_query内容
哪些配置会让 LOCK IN SHARE MODE “看似不生效”?
最常见原因是锁根本没加上——事务没开启,或者 autocommit = 1。每条语句自成事务,锁在语句结束就释放,后续 UPDATE 完全不受保护。
另一个隐蔽问题是主从复制:默认 statement-based binlog 不记录共享锁,从库不会复现锁行为。如果业务逻辑依赖锁做预占(比如库存),主库串行、从库并发,结果就是数据不一致。
- 必须确认:
SELECT @@autocommit返回0,SELECT @@tx_isolation返回REPEATABLE-READ - 主从复制必须用
binlog_format = ROW,且从库tx_isolation保持一致 -
innodb_lock_wait_timeout默认 50 秒,线上建议设为5或10,避免连接池被长等待拖垮
什么时候该换用 SELECT ... FOR UPDATE?
当你读完马上要改,就别分两步走。用 SELECT ... LOCK IN SHARE MODE + UPDATE 存在时间窗口:A 读→B 读→A 更新→B 更新,覆盖写风险高;而 SELECT ... FOR UPDATE 一步加排他锁,天然阻断其他读写,更安全也更简单。
但要注意:它同样受索引影响。范围查询(WHERE created_at > '2026-06-01')会锁住间隙,可能比你预期的多锁几行;等值查主键才真正只锁一行。
- 适合场景:
SELECT balance FROM accounts WHERE user_id = 123 FOR UPDATE→ 扣款 - 避免混用:
SELECT ... LOCK IN SHARE MODE和SELECT ... FOR UPDATE在同一事务里,容易因锁升级顺序不一致引发死锁 - 性能提示:若高频更新某字段,确保
WHERE条件走索引,否则退化为表锁——EXPLAIN中type = ALL就是危险信号
实际用的时候,锁不是加了就万事大吉,得看它到底锁了什么、在什么上下文里生效、以及上下游系统是否同步认这个锁。


















