mysql不存在隐式锁主动转换为显式锁的过程;所谓“转换”实为其他事务访问时触发的被动唤醒机制,即冲突方检查到活跃trx_id后临时补建显式锁结构并进入等待。

隐式锁不是“转换”,而是被其他事务“唤醒”
MySQL 中根本不存在“隐式锁主动升级为显式锁”的过程。所谓“转换”,其实是当另一个事务尝试访问某条被当前事务隐式影响的记录时,InnoDB 才会临时为前者构造一个显式锁结构,并让后者“被动持有”这个锁——这个动作叫 lock_clust_rec_cons_read_sees 或 lock_rec_lock 唤醒机制,不是原事务在执行 INSERT/UPDATE 时做了什么,而是冲突方触发的。
常见误解来源是看到 SHOW ENGINE INNODB STATUS 里出现类似这样的片段:
WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 123 page no 456 n bits 72 index PRIMARY of table `test`.`t` trx id 422037508068888 lock_mode X locks rec but not gap insert intention waiting
这说明:session 2 想插入某行,但 session 1 已经插入了同一位置(未提交),InnoDB 此时才给 session 1 的那条新记录补上一个真实 X RECORD LOCK,并让 session 2 等待——你看到的“显式锁”,是 session 2 的请求催生出来的,不是 session 1 主动申请的。
什么操作会触发隐式锁的“显式化”
只有满足以下全部条件时,隐式锁才会在日志或锁视图中“浮现”出来:
- 当前事务已执行
INSERT(主键/唯一索引值存在冲突可能)或UPDATE(针对聚簇索引记录,且该记录的trx_id字段指向一个活跃事务) - 另一事务执行了与之冲突的操作,例如:
SELECT ... LOCK IN SHARE MODE、SELECT ... FOR UPDATE、UPDATE或DELETE同一记录 - 被访问的记录属于聚簇索引(即有
trx_id隐藏列),且该列值对应一个未提交事务 - 冲突事务在加锁前检查到该
trx_id是活跃的,于是帮原事务“补建”一个锁结构,自己进入等待
注意:INSERT INTO t VALUES (1, 'x') 本身不写入锁系统;但若此时另一事务执行 SELECT * FROM t WHERE id = 1 FOR UPDATE,就会立刻触发显式锁注册,并出现在 performance_schema.data_lock_waits 中。
为什么你在 SHOW ENGINE INNODB STATUS 里看不到“隐式锁”字样
InnoDB 的隐式锁不占用锁队列、不注册到 INNODB_LOCKS(已废弃)、也不出现在任何系统表中。它只存在于内存判断逻辑里,表现为:
- 一条新插入记录的
trx_id字段非零且指向活跃事务 - 没有对应的
LOCK_TRX_ID、LOCK_MODE等元数据 - 只有当其他事务来读/写这条记录时,InnoDB 才临时分配锁结构、填充锁信息、并把等待关系写入状态输出
换句话说:你永远看不到“隐式锁正在运行”,只能看到“它刚刚被撞出来了”。这也是为什么用 SELECT * FROM performance_schema.data_locks 查不到刚插入未冲突的记录锁——它压根没被创建过。
别指望靠隐式锁做业务控制
隐式锁是 InnoDB 内部优化手段,不可观测、不可预测、不可依赖。如果你需要确定性的锁行为,必须显式声明:
- 要串行修改某行 → 必须用
SELECT ... FOR UPDATE或UPDATE ... WHERE pk = ?(在事务内) - 要防止幻读 → 不能只靠 INSERT 的隐式间隙保护,得用
SELECT ... FOR UPDATE配合范围条件 - autocommit=1 下的单条
UPDATE加的是瞬时锁,其他事务几乎无法感知,等于没锁 -
LOCK TABLES是表级锁,和隐式锁无关,也破坏 MVCC,慎用
真正容易被忽略的点是:隐式锁只对聚簇索引记录有效,普通二级索引更新、全表扫描类 UPDATE、无索引 WHERE 条件等场景,InnoDB 会直接走显式加锁路径,根本不会走隐式逻辑。











