SELECT FOR UPDATE 未锁住行的根本原因是事务未开启或autocommit=1;它仅在显式事务中生效,且依赖索引与隔离级别——RR下加间隙锁+记录锁,无索引则锁全表。

SELECT FOR UPDATE 为什么没锁住行
根本原因通常是事务未开启或自动提交未关闭。MySQL 的 SELECT FOR UPDATE 只在显式事务中生效,且必须搭配 START TRANSACTION 或 BEGIN 使用;如果 autocommit=1(默认),每条语句执行完立即提交,锁会立刻释放。
- 执行前务必确认:
SELECT @@autocommit;
返回0才安全;若为1,需先执行SET autocommit = 0; - 不能在视图、临时表、非事务引擎(如 MyISAM)上使用,否则报错
ERROR 1288: The target table ... of the FOR UPDATE clause cannot be updated - 锁定范围取决于 WHERE 条件是否命中索引:全表扫描时可能升级为表锁;无索引字段查询会锁全表,极大降低并发性
SELECT FOR UPDATE 在可重复读(RR)隔离级别下的行为
MySQL 默认隔离级别是 REPEATABLE READ,此时 SELECT FOR UPDATE 使用的是**间隙锁(Gap Lock)+ 记录锁(Record Lock)**,不仅锁住匹配的行,还会锁住索引间隙,防止幻读。
- 例如表
t(id PK, name),执行SELECT * FROM t WHERE name = 'Alice' FOR UPDATE,若name无索引,则锁全表;若有索引但值不存在,仍会锁住该间隙(比如 'Alice' 应该插入的位置) - 间隙锁不可被
SELECT ... LOCK IN SHARE MODE兼容,但可被其他SELECT FOR UPDATE阻塞——这是死锁高发场景 - 想禁用间隙锁?只能临时切到
READ COMMITTED级别(SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED),但会失去幻读防护
如何验证某行是否已被 SELECT FOR UPDATE 锁定
最直接的方式是用另一会话尝试相同语句并观察阻塞行为,但生产环境不宜盲试。更稳妥的方法是查 INFORMATION_SCHEMA.INNODB_TRX 和锁信息表:
- 查看当前活跃事务:
SELECT trx_id, trx_state, trx_started, trx_query FROM INFORMATION_SCHEMA.INNODB_TRX;
- 关联锁信息定位被锁行:
SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_thread, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread, b.trx_query blocking_query FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS w INNER JOIN INFORMATION_SCHEMA.INNODB_TRX b ON b.trx_id = w.blocking_trx_id INNER JOIN INFORMATION_SCHEMA.INNODB_TRX r ON r.trx_id = w.requesting_trx_id;
- 注意:这些表只显示 InnoDB 层面的锁,不包含表级锁或元数据锁(MDL)
SELECT FOR UPDATE 和 UPDATE 的锁区别
SELECT FOR UPDATE 是“纯读锁”,不修改数据,但会加写锁(X 锁),阻止其他事务读写该行;而 UPDATE 语句本身隐式执行类似逻辑,但多了数据变更和 undo log 写入开销。
- 若后续确定要更新,推荐直接用
UPDATE ... WHERE ...,避免多一次锁获取;SELECT FOR UPDATE更适合“读-判-写”三步逻辑,比如库存扣减前先校验余额是否充足 -
UPDATE在 RR 下也会加间隙锁,但仅当 WHERE 条件涉及索引且存在匹配行时才触发;而SELECT FOR UPDATE即使 WHERE 不匹配(如WHERE id = 999999不存在),只要索引可用,仍会锁间隙 - 两者都受
innodb_lock_wait_timeout控制,默认 50 秒,超时抛出ERROR 1205: Deadlock found when trying to get lock或ERROR 1205: Lock wait timeout exceeded
实际应用中最容易忽略的是索引有效性与隔离级别的耦合影响——同一句 SELECT FOR UPDATE,在有无索引、RR 与 RC 下,锁的粒度和范围可能天差地别。










