触发器不能防止死锁,反而易引发死锁,因其隐式执行、不可控加锁顺序;应显式统一锁序、缩短事务生命周期,并用定时任务或cdc替代触发器。

死锁不是触发器能防住的,它只会让问题更隐蔽
触发器不能防止死锁,反而常是死锁的帮凶。数据库加锁发生在语句执行时,而触发器在语句内部自动触发、隐式开启新语句,相当于把多个表的锁请求塞进同一事务里,还无法控制顺序——这正是死锁高发场景。
真正该做的:显式控制锁顺序 + 缩短事务生命周期
死锁本质是多个事务以不同顺序争抢相同资源。解决核心就两条:所有事务按固定表顺序加锁,让 UPDATE/DELETE 尽早完成,别拖着事务干别的。
- 统一约定修改顺序,比如总是先
UPDATE users,再UPDATE orders,最后UPDATE logs;跨服务也要对齐这个顺序 - 避免在事务里调用 HTTP 请求、生成 PDF、发邮件等耗时操作——这些会让持有锁的时间从毫秒拉长到秒级
- 读已提交(
READ COMMITTED)隔离级别下,普通SELECT不加锁,但SELECT ... FOR UPDATE会,务必确认是否真需要锁 - 用
SHOW ENGINE INNODB STATUS查最近死锁详情,重点关注WAITING FOR THIS LOCK TO BE GRANTED和HOLDS THE LOCK(S)两段,它们直接暴露了谁锁了什么、谁在等什么
触发器为什么容易引发死锁?
触发器执行不可见、不可控,且默认复用当前事务上下文。一个看似简单的 INSERT INTO orders,若带 AFTER INSERT 触发器去更新 users 余额,就等于在同一个事务里按「orders → users」顺序加锁;而另一处业务代码显式按「users → orders」更新,冲突就定了。
- 触发器里的 SQL 无法被外部事务顺序策略覆盖,它自己决定锁哪张表、什么时候锁
- MySQL 8.0+ 虽支持
LOCK IN SHARE MODE在触发器中显式加锁,但只会加剧竞争,不解决根本问题 - 想审计或补数据?用定时任务或 CDC 工具(如 Debezium)替代触发器,把写操作彻底拆出事务边界
哪些场景下宁可不用触发器,也要手动控制?
涉及资金、库存、积分等强一致性要求的更新,或者需跨微服务协同的状态变更,触发器基本属于高危选项。
-
UPDATE inventory SET stock = stock - 1 WHERE sku = 'A123'后,必须同步更新sales_summary—— 改成应用层用单条事务完成,或用消息队列异步补偿 - 用户注册后要初始化配置、发欢迎邮件、记录行为日志 —— 邮件和日志写入必须剥离出主事务,否则一个 SMTP 超时就能卡住整个注册流程
- ORM 如 Django 或 Laravel 的
post_save信号,本质也是触发器逻辑,同样适用上述原则:信号处理器里禁止 DB 写操作,只发消息或记队列
最麻烦的不是写不出触发器,而是线上跑了三个月才发现某条路径下它和另一个批处理作业总在争同一行记录——这种问题不会报错,只会间歇性超时,查起来费劲。










