mysql中使用SELECT FOR UPDATE语句进行行级锁定

来源:这里教程网 时间:2026-02-28 20:42:23 作者:

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 下,锁的粒度和范围可能天差地别。

相关推荐