next-key lock是innodb在rr隔离级别下为防幻读采用的锁机制,由记录锁与间隙锁组合而成,范围为左开右闭区间,仅在rr级别生效。

大事务在REPEATABLE READ下锁范围太广
MySQL默认隔离级别是REPEATABLE READ,InnoDB对UPDATE和DELETE语句使用Next-Key Lock——既锁匹配行,又锁间隙。一个大事务执行UPDATE orders SET status = 'done' WHERE created_at > '2026-08-01',若created_at无索引或只有非唯一索引,可能锁住成百上千行+它们之间的全部间隙。其他事务只要碰这个范围(比如插入新订单、更新其中某行),就会被阻塞;而它自己又在等别的锁,死锁检测器一扫描就发现环路。
锁持有时间长 + 高并发 = 死锁概率指数上升
大事务常包含HTTP调用、日志写入、循环计算等外部操作,导致事务持续数秒甚至十几秒。这期间持有的锁不会释放,其他并发事务不断尝试获取同一资源,排队等待链越拉越长。一旦两个事务以不同顺序访问相同数据(如事务A先锁user_id=100再锁order_id=500,事务B反向),死锁检测器每秒运行一次,几乎必然捕获到循环等待。
- 事务越长,被选为死锁“victim”的概率越高(InnoDB优先回滚
undo log最小的事务) - 高峰期并发请求翻倍,锁等待队列从线性增长变成网状交织,检测命中率飙升
-
SHOW ENGINE INNODB STATUS\G里LATEST DETECTED DEADLOCK段会密集出现,且持锁资源往往跨多张表(含触发器隐式更新的表)
innodb_lock_wait_timeout掩盖了真实问题
很多人把Lock wait timeout exceeded(错误1205)和死锁(错误1213)混为一谈。前者是等锁超时,后者是检测到循环等待后主动杀掉一个事务。把innodb_lock_wait_timeout从50秒调低到10秒,看起来“报错更快”,但只是让失败更早暴露,并未减少死锁发生次数——它只是把原本要等10秒才触发的死锁,提前到第3秒就爆出来而已。
- 真正该调的是事务行为,不是超时参数
- 频繁看到错误1213,说明锁序混乱或范围锁失控,不是靠改
innodb_lock_wait_timeout能解决的 - 如果
SHOW ENGINE INNODB STATUS里反复出现相同SQL组合(如UPDATE users和UPDATE orders交替出现在HOLDING/WAITING中),就是典型的锁序不一致信号
触发器会让锁路径彻底不可见
一个看似简单的INSERT INTO orders,如果带AFTER INSERT触发器去更新user_stats和inventory_log,实际会变成三张表的复合加锁操作,且共享同一事务ID。你从应用日志只看到一条INSERT,但InnoDB状态里会显示mysql tables in use 3, locked 3——这种隐式锁扩展极大增加交叉等待风险,而且排查时容易漏掉触发器里的SQL。
- 触发器内
UPDATE必须走主键或唯一索引,禁用WHERE status = 'pending'这类范围条件 - 用
EXPLAIN FORMAT=TRADITIONAL验证触发器内每条DML是否命中索引,type不能是ALL或index - 涉及统计值更新,优先用
INSERT INTO summary_log(BLACKHOLE引擎)替代实时UPDATE,把聚合延迟到异步任务










