触发器仅适用于数据变更即响应、轻量确定、数据库内闭环的场景;须满足“数据一变,逻辑即响”,仅依赖new/old行及本库索引字段,禁用外部调用、复杂查询、循环与递归,错误导致事务回滚,需强调试与兜底能力。

不适合封装复杂流程、外部调用或依赖应用上下文的逻辑,只适合数据变更即需响应、轻量确定、数据库内可闭环的场景。
看是否满足“数据一变,逻辑即响”这个前提
触发器不是业务服务,它不接收参数、不感知 HTTP 请求头、不知道当前登录用户是谁。它只响应 INSERT/UPDATE/DELETE 这三类操作,并且必须能仅凭 NEW 和 OLD 行数据(以及本库其他表的索引字段)完成全部判断和动作。
- 适合:订单状态变为
'shipped'时,自动将对应物流单的is_dispatched设为true - 不适合:订单发货后,调用短信网关通知用户——触发器拿不到手机号归属渠道、模板变量、发送频控策略
- 不适合:插入用户时,根据“当前请求来源”决定写入哪个分片表——
NEW里没有这个字段,也无法从会话中提取
看是否能用单条 SQL 或极简逻辑闭环
触发器里每多一层嵌套查询、每次跨表 JOIN、每个 COUNT(*) 判断,都会放大性能风险。高并发下容易拖慢主 SQL,甚至锁表。
- 可用
EXISTS (SELECT 1 FROM users WHERE id = NEW.user_id AND is_active = 1)—— 只要命中索引,快且安全 - 禁用
SELECT COUNT(*) FROM orders WHERE user_id = NEW.user_id AND status = 'pending'—— 全表扫描,还可能被优化器误判为不可缓存 - 禁用循环逻辑(如 MySQL 不支持游标在触发器中遍历结果集),也禁用递归调用自身或其他触发器
看错误是否允许“静默失败”或“事务级回滚”
触发器报错 = 原始 SQL 失败。如果你的业务能接受“这条数据暂时写不进去”,那可以;如果这是日志类旁路操作,就不该放触发器里。
- 适合用
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '客户余额不足'中断插入 - 不适合在触发器里写
INSERT INTO notification_queue然后指望异步消费——一旦队列表写失败,整个订单插入就失败 - 审计日志这类旁路操作,更推荐由应用层统一发 Kafka 或落盘,而非塞进触发器强一致写入
看团队是否具备调试和兜底能力
触发器执行不可见、堆栈无行号、批量操作易出错。上线前没做多行模拟测试、没关掉导入时的触发器、没加 IF TG_OP = 'UPDATE' 判断,都可能引发线上事故。
- 必须有手段验证它真被触发了:比如 PostgreSQL 查
pg_stat_statements,MySQL 临时写INSERT INTO debug_log - 必须测试批量场景:
INSERT INTO orders SELECT ... FROM temp_batch会逐行触发,不能假设只跑一次 - 必须准备兜底方案:比如误触发导致计数翻倍,得靠定时校验脚本或备份比对来发现











