mysql 8.0中show processlist里lock wait状态减少,是因为其innodb层采用更细粒度锁等待队列管理,配合performance_schema.data_lock_waits快速识别并裁剪无效等待路径,使高并发下锁等待平均时间下降30%~40%,而非锁本身减少。

MySQL 从 5.7 升级到 8.0 后锁争用减少,不是因为“锁变少了”,而是因为 innodb_deadlock_detect 默认仍开启、但底层加锁逻辑和等待处理更高效,同时新增的 SKIP LOCKED 和 NOWAIT 让应用层能主动绕过锁冲突——关键改善点在“可控性”和“响应速度”,而非锁本身消失。
为什么 SHOW PROCESSLIST 里 Lock Wait 状态变少了
5.7 中事务请求行锁失败时,会直接进入无差别等待队列,直到超时或被唤醒;8.0 在 InnoDB 层增加了更细粒度的锁等待队列管理,配合性能模式(performance_schema.data_lock_waits)可快速识别并裁剪无效等待路径。实际效果是:同样高并发 UPDATE 场景下,8.0 的锁等待平均时间下降约 30%~40%,大量短等待被合并或提前释放。
- 默认
innodb_lock_wait_timeout仍是 50 秒,但内部等待调度更激进,尤其在 RR 隔离级别下对间隙锁(Gap Lock)的持有范围做了保守收缩 -
SELECT ... FOR UPDATE在唯一索引等值查询时,8.0 能更准确降级为记录锁(Record Lock),避免误触临键锁(Next-Key Lock) - 如果业务中用了
ORDER BY ... LIMIT配合FOR UPDATE,8.0 会优先对结果集预判加锁范围,而不是全扫描加锁
SKIP LOCKED 是怎么缓解锁争用的
它不减少锁,而是让“抢不到锁”的查询不卡住——典型用于任务队列、订单分发等场景。5.7 没有这个语法,应用只能靠重试或加分布式锁兜底;8.0 原生支持后,同一张表上多个消费者并发执行 SELECT ... FOR UPDATE SKIP LOCKED,彼此互不阻塞,吞吐量直线上升。
- 必须搭配
FOR UPDATE或LOCK IN SHARE MODE使用,单独SKIP LOCKED无效 - 不会跳过已被其他事务
COMMIT但尚未刷盘的变更(即不破坏一致性) - 在主从架构下,从库不支持该语法(只读实例会报错),需确保路由到主库执行
为什么你升级后反而遇到更多死锁报警
这不是锁变多了,而是 8.0 的死锁检测更灵敏、上报更完整。5.7 中部分循环等待可能被忽略或延迟上报;8.0 默认启用 innodb_deadlock_detect=ON,且日志中会输出完整的事务堆栈(SHOW ENGINE INNODB STATUS 的 LATEST DETECTED DEADLOCK 区块更详细)。
- 常见诱因没变:跨事务加锁顺序不一致、非唯一索引上的范围更新、触发器隐式加锁
- 但 8.0 新增了
innodb_print_all_deadlocks=ON配置,所有死锁都会写入错误日志,导致“感觉变多” - 若确实高频死锁,优先检查是否用了
UPDATE ... WHERE non_unique_index_col = ?——这种语句在 8.0 中更容易因间隙锁扩大而引发竞争
真正容易被忽略的是:8.0 对“锁等待”的定义更严格,比如元数据锁(MDL)等待现在会被明确标记为 Waiting for table metadata lock,而 5.7 可能混在 Locked 里难以区分。排查时别只盯 State 列,一定要结合 Info 字段和 Time 值交叉判断。











