MySQL 8.0中SELECT FOR UPDATE锁更多行,是因为移除了innodb_locks_unsafe_for_binlog开关,强制所有此类语句加Next-Key Lock(记录锁+间隙锁),不再退化为仅记录锁;同时优化器对隐式类型转换更敏感、间隙锁范围更精确,导致锁范围扩大、插入阻塞增多。

为什么升级后 SELECT FOR UPDATE 突然锁更多行?
不是语义变了,而是 innodb_locks_unsafe_for_binlog 这个开关在 8.0 彻底移除,所有 SELECT FOR UPDATE 和 SELECT LOCK IN SHARE MODE 都严格按索引加 Next-Key Lock(记录锁 + 间隙锁),不再退化为仅记录锁。
- 5.7 中用非唯一索引字段做
UPDATE WHERE non_unique_col = ?,可能只锁命中行;8.0 默认锁整个间隙,高并发插入时容易阻塞 - WHERE 条件含隐式类型转换(如
WHERE varchar_id = 123),8.0 优化器更早放弃索引、触发全表扫描+锁升级,而 5.7 可能侥幸走索引少锁 - 查不到数据时也锁间隙:5.7 查询未命中行可能只锁索引项;8.0 直接锁该值对应间隙,后续相同条件的
INSERT会被卡住
为什么 SHOW ENGINE INNODB STATUS 里事务长时间卡在 ACTIVE?
大概率不是死锁,而是被 DDL 操作阻塞。MySQL 8.0 的原子 DDL 要求更强的元数据锁(MDL),哪怕只是普通 REPEATABLE READ 事务,也可能被 ALTER TABLE 卡住更久。
- 错误日志里出现大量
Waiting for table metadata lock,通常和事务隔离无关,是 DDL 正在执行时,监控脚本查sys.innodb_lock_waits被 MDL 卡住 -
SHOW ENGINE INNODB STATUS\G的 TRANSACTIONS 区段中,若事务无 SQL 正在执行却长期处于 ACTIVE,优先排查是否正在执行ALTER、CREATE INDEX等操作 - 升级后
sys.innodb_lock_waits查询变慢甚至阻塞,是因为它底层依赖performance_schema.data_locks,读取时会申请 MDL 锁——大锁场景下该视图本身就会成为瓶颈
为什么死锁报错突然变多?
不是死锁变多了,而是 innodb_deadlock_detect 在 8.0 默认开启,把原来静默忽略的环形等待全部检测并中止。5.7 中这个参数常被手动关闭,尤其在高并发写场景。
- 错误日志里密集出现
Deadlock found when trying to get lock,先别急着关innodb_deadlock_detect——关掉只会让问题更隐蔽,最终表现为连接堆积、响应延迟 - 用
SHOW ENGINE INNODB STATUS\G看LATEST DETECTED DEADLOCK区块,确认是否真有循环等待:事务 A 持有table_a行锁却等table_b,事务 B 持有table_b却等table_a - 批量更新用
IN (1,2,3,1000)但没走索引,8.0 的间隙锁范围更大,多个事务扫同一区间极易形成循环等待;应用层必须对 IN 列表强制排序,比如ORDER BY id ASC
为什么锁监控视图查询变慢甚至拖垮业务?
因为 sys.innodb_lock_waits 在 8.0 底层改用 performance_schema.data_locks,它记录“所有事务持有的锁”,而非 5.7 中仅记录“阻塞其他事务的锁”。当存在大锁操作(如锁 300 万行),data_locks 表瞬间生成海量记录,读取时持有 trx_sys->mutex 全局互斥量,导致新事务无法加入全局链表。
- 监控脚本每分钟执行一次
SELECT * FROM sys.innodb_lock_waits,在锁量大的情况下,会与业务 SQL 形成资源竞争恶性循环 - 临时缓解:禁用该监控,或改用轻量方式,例如只查
INFORMATION_SCHEMA.INNODB_TRX中trx_wait_started超过阈值的事务 - 真正容易被忽略的是:
data_locks表膨胀不报警、不告警,但它对全局事务链表的锁争用是静默扼杀型的——CPU 和 IO 都低,但 SQL 就是排队不动


















