死锁检测是被动触发而非定时任务,每次加锁失败即构建wait-for图并执行dfs遍历;热点行争抢会导致高频检测,图过大时innodb主动放弃检测而回滚事务,此时cpu飙升但无死锁日志,本质是事务密集争抢同一行。

死锁检测不是定时任务,而是每次锁失败就触发
很多人以为 innodb_deadlock_detect 是后台定时扫描,其实它完全被动:只要一个事务在加锁时发现目标行已被占用、且等待队列里已有其他事务,InnoDB 就立刻构建 Wait-for Graph 并执行 DFS 遍历。这意味着——热点行(比如库存、账户余额)被 100 个事务排队争抢,就会触发 100 次图构建 + DFS,而不是 1 次。
Wait-for Graph 构建和 DFS 都是内存密集型操作
图结构本身要维护节点(事务)、有向边(A→B 表示 A 等待 B 的锁),每次增删都要哈希查找、链表插入、加锁保护;DFS 遍历更吃 CPU:图中事务数超 200 或单事务需检查的锁超 1,000,000 个时,InnoDB 会直接放弃检测、回滚当前事务——这不是“没检测到”,而是怕遍历太慢主动投降。实际中,50 个事务互相部分等待,DFS 时间复杂度就接近 O(N²)。
CPU 飙高但 SHOW ENGINE INNODB STATUS 不报死锁
这是典型信号:TRANSACTIONS 段里反复出现 TRX HAS BEEN WAITING,且锁对象高度重复(如都卡在 PRIMARY 上同一行),但 LATEST DETECTED DEADLOCK 段为空。说明检测高频发生,但多数没成环就被提前终止了。此时 innodb_row_lock_waits 持续上涨,innodb_row_lock_time_avg 却很低——不是锁持有久,是抢锁失败太频繁。
关掉 innodb_deadlock_detect 只是掩盖问题
设为 OFF 后,CPU 可能下降,但代价是所有死锁不再被主动识别,事务只能等 innodb_lock_wait_timeout(默认 50 秒)超时才回滚。你会看到大量 Lock wait timeout exceeded,SHOW PROCESSLIST 里堆满 Locked 状态,连接池耗尽,下游雪崩。真正该做的不是关检测,而是让事务别进等待图:把热点更新挪到事务末尾、拆分逻辑账户、用乐观锁 + 条件更新替代 SELECT FOR UPDATE。











