
MySQL 8.0的死锁检测默认开启且更激进
5.7 默认关闭 innodb_deadlock_detect(尤其在高并发写场景下常被手动关掉),而 8.0 默认为 ON。这不是“变多”,是“终于报出来了”——原来被静默忽略的潜在死锁,现在全被检测并中止,错误日志里密集出现 Deadlock found when trying to get lock。
别急着调低灵敏度,先确认是否真有逻辑问题:
检查错误日志里死锁事务的 SQL 模式:是否总在相同两张表、相同 WHERE 条件、不同执行顺序下触发?用 SHOW ENGINE INNODB STATUS\G 抽取最近一次死锁详情,重点看 TRANSACTIONS 部分列出的两个事务各自持有的锁(HELD LOCKS)和等待的锁(WAITING FOR THIS LOCK TO BE GRANTED)若发现事务 A 持有 table_a 行锁却等待 table_b,而事务 B 持有 table_b 却等待 table_a,就是典型锁序混乱,不是配置问题,是代码问题
行锁等待队列公平性提升反而暴露旧有“饥饿”逻辑
8.0 改进了行锁唤醒策略:不再优先唤醒最早等待的事务,而是按等待资源的“公平性”调度。这导致过去靠“抢跑”侥幸成功的长事务,在 8.0 下更容易被卡住、超时,进而触发连锁阻塞或重试风暴,间接推高死锁感知频率。
重点关注以下几类语句:
SELECT … FOR UPDATE 后未及时 COMMIT 或 ROLLBACK 的会话(查 information_schema.INNODB_TRX 中 trx_started 时间过早的记录)批量更新中用了 IN (1,2,3,…1000) 但没走索引,导致间隙锁(gap lock)范围过大,多个事务同时扫描同一区间时极易形成循环等待应用层重试逻辑未设上限,一个死锁失败后立即重试,又撞上同样锁竞争,形成“死锁→重试→再死锁”循环
sys.innodb_lock_waits 视图行为变更引发隐式阻塞
这是升级后最隐蔽的坑:5.7 的 sys.innodb_lock_waits 是纯视图,不加锁;8.0 中它底层依赖 performance_schema.data_locks 和 data_lock_waits,而这两个表在读取时会主动申请 MDL 锁 —— 如果此时恰有 DDL 正在执行(比如加字段、重建索引),监控脚本就会卡在 Waiting for table metadata lock,进而拖慢整个连接池响应,业务请求排队堆积,锁等待时间拉长,死锁概率自然上升。
临时缓解方法:
停用所有基于 sys.innodb_lock_waits 的定时巡检(尤其是每分钟跑一次的监控脚本)改用轻量替代方案:定期查 SELECT * FROM performance_schema.events_statements_current WHERE SQL_TEXT LIKE ‘%FOR UPDATE%’,或直接抓 SHOW PROCESSLIST 中状态异常的线程确认无 DDL 运行时再启用锁视图,切勿在升级后首小时运行
原子 DDL 日志机制引发建表阶段 MDL 卡顿
8.0 引入 mysql.ibd + ddl_log 表实现 DDL 原子性,但该机制强依赖 redo 日志稳定性。若升级后立刻执行大量 CREATE TABLE(如微服务自动建表、ETL 初始化),可能因 innodb_redo_log_capacity 不足或磁盘 I/O 波动,导致 DDL 日志写入 pending,进而使新表的 MDL 无法释放 —— 后续所有对该库的 SELECT/INSERT 都会被阻塞,表现就像“全局锁住”,连带放大死锁感知。
必须做的动作:
升级后首 60 分钟内禁止任何自动化建表任务(包括 ORM 的 syncdb、Flink CDC 的 auto-DDL)检查 SHOW ENGINE INNODB STATUS 输出中的 LOG 小节,确认 pending log writes 为 0若已发生卡顿,不要 kill 线程,而是用 KILL QUERY [thread_id] 中止具体 SQL,避免中断 DDL 导致元数据损坏
真实死锁从来不在参数里,而在你没意识到的锁序和没及时提交的事务里。升级后第一件事不是调 innodb_deadlock_detect,是翻错误日志里那几条最频繁的死锁堆栈,顺着 SQL 找到对应的应用代码段——那里才藏着真正的开关。

评论(0)