MySQL 8.0并非锁变多,而是默认开启innodb_deadlock_detect、更严MDL管理及sys.innodb_lock_waits视图改用MDL锁,导致原有环形等待和悬挂事务全面暴露。

不是锁变多了,是原来被忽略的锁等待现在全暴露了——MySQL 8.0 默认开启更激进的死锁检测和更严格的元数据锁管理,同时 sys.innodb_lock_waits 视图底层行为变更导致监控脚本本身变成阻塞源。
innodb_deadlock_detect 默认开启,把“静默环形等待”全报出来
MySQL 5.7 中常手动关闭 innodb_deadlock_detect,升级到 8.0 后默认为 ON,它会主动遍历锁等待图并中止循环等待事务。你看到的大量 Deadlock found when trying to get lock 错误,99% 是原来就存在、只是没被检测到的环形等待。
- 别急着关
innodb_deadlock_detect:关掉只会让问题转为长等待甚至连接堆积,SHOW PROCESSLIST里全是Updating状态却查不到阻塞源 - 确认是否真死锁:用
SHOW ENGINE INNODB STATUS\G查LATEST DETECTED DEADLOCK区块,看两个事务是否互持对方要的锁(如事务 A 持t1.id=1等t2.id=5,事务 B 反之) - 反复出现在日志里的相同表+索引组合?说明应用层加锁顺序不一致,比如
WHERE id IN (3,1,2)和WHERE id IN (2,3,1)导致 InnoDB 加锁物理顺序不同
lock_wait_timeout 默认设成一年,DDL 卡住却不报错
lock_wait_timeout 控制元数据锁(MDL)等待超时,默认值从 31536000 秒(一年)开始生效,而很多人误改 innodb_lock_wait_timeout(只管行锁),结果 DDL 还是卡死。
-
SELECT @@lock_wait_timeout查当前值;线上建议设为30或60,和应用层 read_timeout 对齐 - 改了参数仍卡住?因为该参数只影响「新申请 MDL 的线程」,不中断已持有的锁——真正卡住 DDL 的,90% 是未提交事务或
mysqldump --single-transaction进程没结束 - 快速定位阻塞源:
SELECT * FROM performance_schema.metadata_locks ml JOIN performance_schema.threads t ON ml.OWNER_THREAD_ID = t.THREAD_ID WHERE ml.LOCK_STATUS = 'PENDING'
sys.innodb_lock_waits 查询变慢甚至阻塞业务
MySQL 8.0 中 sys.innodb_lock_waits 不再是纯视图,底层依赖 performance_schema.data_locks 和 data_lock_waits,读取时会申请 MDL 锁——如果监控脚本每分钟执行一次,它自己就成了锁竞争热点。
- 现象:升级后业务 SQL 突然变慢,但 CPU、IO、网络都正常;
SHOW PROCESSLIST里能看到多个线程卡在Waiting for table metadata lock - 验证方法:临时停掉监控脚本,观察业务是否恢复;或者直接查
performance_schema.events_statements_current,看是否有大量SELECT在等data_locks表 - 替代方案:不用
sys.innodb_lock_waits,改用直接查information_schema.INNODB_TRX+performance_schema.data_locks组合,避免触发 MDL 争抢
未提交的悬挂事务在 8.0 下更难隐藏
MySQL 8.0 对事务状态跟踪更严格,wait_timeout 默认仍是 28800 秒(8 小时),而很多 Python/Java 应用用 pymysql 或 aiomysql 连接时默认 autocommit=False,执行完 SELECT 不 commit() 也不 close(),事务就挂着不动,持续持有 MDL 锁。
-
SHOW PROCESSLIST找不到它:状态是Sleep、INFO为空、TIME很大(比如 3600 秒),但它在INFORMATION_SCHEMA.INNODB_TRX中仍显示trx_state = 'RUNNING' - 安全终止方式:先查
INNODB_TRX找trx_query IS NULL AND trx_state = 'RUNNING'的记录,再用KILL QUERY thread_id(无效则再KILL thread_id) - 预防动作必须做:全局设置
wait_timeout = 300和interactive_timeout = 300,否则升级等于放大老问题十倍
最易被忽略的点是:所有这些变化都不是独立发生的——一个未提交的悬挂事务,可能同时触发 lock_wait_timeout 等待、sys.innodb_lock_waits 阻塞、以及 innodb_deadlock_detect 频繁报错。排查时不能只盯单一现象。


















