隐式锁不会转换为显式锁,它仅在其他事务访问被隐式保护的聚簇索引记录时,由后者主动为其补建显式锁结构并进入等待;该过程不涉及原事务主动升级,而是冲突触发的被动补锁。

隐式锁根本不会“转换”,它只是被其他事务“唤醒”
MySQL 里没有“隐式锁升级为显式锁”这回事。所谓“转换”,其实是误解——InnoDB 的隐式锁本质上是**不注册、不排队、不记录**的内存状态,它只在另一个事务试图访问同一行时,由后者主动触发加锁动作。换句话说:不是第一个事务把锁“转”出去了,而是第二个事务发现“这行被别人动过但没明说”,于是帮它补上一个真实锁结构,并把自己挂起等待。
典型现象是:SHOW ENGINE INNODB STATUS 中出现类似这样的片段:
WAITING FOR THIS LOCK TO BE GRANTED: lock_mode X locks rec but not gap insert intention waiting
这说明 session B 想插入某位置,但 session A 刚插了一条还没提交的记录(隐式存在),InnoDB 就让 session B 帮 session A 补了一个 X locks rec but not gap,同时自己进入 waiting 状态。
什么操作会触发这个“唤醒”行为
只有满足以下全部条件时,隐式锁才会被“浮现”成可观察的显式锁:
- 目标记录是聚簇索引(即主键或未定义主键时的 row_id)
- 该记录的
trx_id隐藏列指向一个**活跃未提交事务** - 另一个事务执行了需要加锁的操作,且目标正是这条记录,例如:
SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE、UPDATE、DELETE - 注意:
SELECT(无锁语法)不会触发唤醒;INSERT自身也不会唤醒别人,只会被别人唤醒
为什么你在 INFORMATION_SCHEMA.INNODB_TRX 或 performance_schema.data_locks 里看不到隐式锁
因为隐式锁压根没写入锁系统:
-
INNODB_TRX只显示持有显式锁的事务,隐式锁事务不会出现在这里 -
data_locks表依赖锁队列,而隐式锁不进队列 -
SHOW ENGINE INNODB STATUS是唯一可能间接反映它的渠道,但也只在冲突发生后才显示“已补锁”的结果 - 如果你的 INSERT 没遇到并发读写,它就像没加过锁一样——日志里没痕迹,监控里没指标,连
TRX_ROWS_LOCKED都不会涨
想稳定控制锁?别等隐式锁“被唤醒”,直接写显式语句
依赖隐式锁机制等于把锁行为交给 InnoDB 猜,而它只在冲突时才出手,时机不可控、粒度不可调、调试不可见。真正可控的做法是:
- 要更新某行,先用
SELECT ... FOR UPDATE显式加锁,再UPDATE - 要防插入幻读,确保查询带范围条件(如
WHERE id > 100)并配合FOR UPDATE,否则 InnoDB 可能只锁记录不锁间隙 - 避免在
autocommit=1下单条UPDATE——锁加完立刻释放,别的事务根本来不及感知 - 不要用
LOCK TABLES模拟“锁升级”,它和行级隐式锁不在一个维度:LOCK TABLES是 server 层表锁,会阻塞所有 DML,和 InnoDB 行锁机制无关
最易被忽略的一点:隐式锁只存在于聚簇索引记录上,二级索引记录、非唯一条件、全文索引等场景下,InnoDB 通常直接走显式加锁路径,根本不会启用隐式逻辑。


















