
触发器本身不直接报锁超时,但它会让事务持有锁的时间不可控地延长——只要触发器逻辑没跑完,主事务就无法提交,锁就一直挂着。
触发器把慢操作塞进事务主路径
INSERT/UPDATE/DELETE 执行完不代表事务结束,触发器里的代码也属于同一事务。一旦触发器里有跨库查询、远程调用、标量子查询或游标循环,整个事务就会卡在那儿等结果。
-
WAITFOR、OPENQUERY、EXEC外部存储过程:直接让事务停在数据库层,锁持续释放不了 - 逐行执行的子查询,比如
UPDATE t SET total = (SELECT SUM(amount) FROM items WHERE order_id = t.id):N 行插入 → N 次全表扫描,锁时间线性增长 - 没加索引的关联条件(如
WHERE log.order_id = i.id但log.order_id无索引):每行都触发间隙锁 + 全表扫描,阻塞雪球越滚越大
锁等待被掩盖在原始语句里
你看到的报错是 Lock wait timeout exceeded; try restarting transaction,但真正卡住你的,是触发器内部某条 SQL —— 它不会单独出现在慢日志或 SHOW PROCESSLIST 中,只会在 INFORMATION_SCHEMA.INNODB_TRX 里显示为一个运行了几分钟的“普通”事务。
- 查
INNODB_TRX时重点关注TRX_STARTED和TRX_STATE:如果状态是RUNNING且已运行 >60 秒,大概率是触发器在干活 -
TRX_QUERY显示的是原始 INSERT,不是触发器里的语句 —— 这就是为什么光看 SQL 很难定位 - 用
SELECT * FROM sys.triggers WHERE parent_id = OBJECT_ID('your_table')(SQL Server)或SHOW CREATE TRIGGER(MySQL)快速确认是否存在隐式耗时逻辑
禁用触发器验证比读代码快得多
别花半小时翻应用层通知逻辑,直接在数据库临时关掉它,重放一次操作,看锁超时是否消失 —— 这是最短路径验证方式。
- SQL Server:
DISABLE TRIGGER tr_name ON table_name,验证完用ENABLE TRIGGER恢复 - MySQL:
DROP TRIGGER风险高,改用注释掉触发器体内的核心逻辑(如把INSERT INTO log...改成-- INSERT INTO log...),再ALTER TRIGGER重装 - 注意:禁用后务必补测业务一致性,比如审计日志是否断档、下游系统是否收不到变更
真正危险的不是“用了触发器”,而是把本该异步、幂等、可丢弃的操作,硬塞进强一致、必须成功、不能中断的事务里。哪怕只是发一条钉钉通知,也足以让库存扣减接口在大促时批量超时。











