mysql 8.0 通过拆分 lock_sys->mutex 为 mutex 和 wait_mutex、行锁哈希表分片等优化显著缓解锁系统瓶颈,而 5.7 因全局单锁导致高并发下严重争用。

为什么lock_sys->mutex在5.7里总成瓶颈,而8.0里少见了
MySQL 5.7 的锁管理器用一个全局 lock_sys->mutex 串行保护所有锁操作,高并发下大量线程挤在这一把锁前排队——就像早高峰只开一条ETC通道。实测中,LOCK_grant 或 lock_sys->mutex 常占 perf top 等待栈前两位。
MySQL 8.0 彻底重写 lock_sys_t 结构,核心是“拆”:把单 mutex 拆成 mutex(加锁路径)、wait_mutex(等待图维护)两把独立锁;再把行锁哈希表按空间分片,用 rec_hash 和 prdt_hash 分别管理记录锁与预测锁。这相当于把ETC通道扩成多车道+分车型专用道。
- 默认分片数由
innodb_sync_array_size控制(默认16),若观察到performance_schema.data_locks中某几个 page_no 集中出现在多个lock_rec_t记录里,说明 hash 冲突高,可调大该值 - 旧监控脚本若硬解析
SHOW ENGINE INNODB STATUS的 LOCK WAIT 段落,大概率失败——8.0 输出格式已重构,字段顺序、缩进、嵌套层级全变 - 这个优化不降低单次加锁开销,只缓解争用;对低并发或冷数据更新几乎无感知
innodb_row_lock_waits 下降但 innodb_row_lock_time_avg 升高?查sys.innodb_lock_waits前先看这个
别急着查谁堵了谁。先确认是不是锁系统自身争用被误判为业务锁等待——8.0 的 innodb_row_lock_time_avg 统计包含锁分配器内部等待时间(比如等 wait_mutex),而 5.7 只统计事务间真实行锁等待。
所以你可能看到:压测 QPS 上去后,innodb_row_lock_waits 增速变缓(说明 lock_sys 争用少了),但 innodb_row_lock_time_avg 反而跳升——这不是业务 SQL 慢了,而是锁分配器分片不均或 wait_mutex 被频繁抢夺。
- 优先查
performance_schema.events_waits_summary_global_by_event_name,过滤wait/synch/mutex/innodb/lock_sys_wait_mutex的COUNT_STAR和SUM_TIMER_WAIT - 若该事件等待次数远高于
wait/synch/mutex/innodb/lock_sys_mutex,说明死锁检测线程(srv_error_monitor_thread)在高并发下持续抢锁,可临时关掉innodb_deadlock_detect=OFF观察 -
sys.innodb_lock_waits只反映事务级阻塞链,不包含 lock_sys 内部等待;它查不到的“慢”,很可能就卡在这儿
备份脚本里还有FLUSH TABLES WITH READ LOCK?8.0会直接报错
MySQL 8.0 的 MDL 锁语义更严格,FLUSH TABLES WITH READ LOCK 会被拒绝执行,并抛出 ER_MDL_DEADLOCK 或 ER_CANT_DO_THIS_DURING_AN_TRANSACTION 错误——不是 bug,是设计变更。
根本原因是:该命令需要获取全局 MDL_EXPLICIT 锁,而 8.0 要求所有 DDL/DML 必须显式声明锁强度,且不允许低优先级操作插队。备份工具若仍依赖 FTWRL,会在 8.0 上静默失败或卡住。
- 替代方案:用
mysqldump --single-transaction(需引擎支持 MVCC,InnoDB 可用) - 物理备份改用
mysqlbackup或Percona XtraBackup,它们绕过 MDL 直接拷贝文件 - 若必须用 FTWRL(如 MyISAM 表混存),得在 8.0 上显式设
skip_log_bin=ON并确保无长事务,否则极易触发超时
死锁响应从秒级到毫秒级,代价是CPU吃紧
8.0 默认开启 innodb_deadlock_detect=ON,后台线程持续构建和扫描等待图(wait-for graph),一旦发现环立即回滚。好处是死锁不再靠 innodb_lock_wait_timeout 被动触发,响应稳定在毫秒级。
坏处也很实在:每毫秒都要遍历当前所有活跃事务的锁依赖关系,高并发短事务场景下,srv_error_monitor_thread CPU 占用可飙升 20%~30%。
- 若应用层已有重试逻辑(如电商扣库存失败后自动重试),可关掉死锁检测:
SET GLOBAL innodb_deadlock_detect=OFF - 关掉后死锁不会消失,只是退回到 5.7 模式:靠超时被动发现,但 CPU 更稳
- 注意:关掉后
innodb_deadlocks状态变量将不再增长,不能用它判断死锁发生频率
真正难调的从来不是锁本身,而是锁系统与业务节奏的耦合点——比如一个 200ms 的库存更新,在 8.0 里可能因 wait_mutex 抢夺多等 15ms,而这段延迟在监控里既不报错也不告警,只默默拖垮 P99 延迟。











