绝大多数业务逻辑不该放触发器里,它只适合日志记录、状态快照等旁路保障,不适合主流程决策;滥用会导致调试难、事务耦合紧、执行不可见等问题。

触发器该不该用来做业务逻辑?
不能一概而论,但绝大多数业务逻辑不该放触发器里。它适合做「旁路保障」——比如日志记录、状态快照、跨表一致性校验;不适合做「主流程决策」,比如判断用户是否可下单、调用外部服务、执行复杂分支逻辑。一旦把核心业务规则塞进 INSERT 或 UPDATE 触发器,调试难、测试难、回滚难,连 EXPLAIN 都看不出它在干啥。
- 触发器执行不可见:应用层看不到它偷偷改了什么,
select结果可能和你刚INSERT的数据对不上 - 错误堆栈不友好:报错时只显示
Error: trigger procedure ... failed,没行号、没上下文 - 事务耦合紧:触发器和原 sql 共享同一事务,它出错整个语句回滚,但你未必想让主操作也失败
mysql vs postgresql 触发器写法差异在哪?
关键不在语法糖,而在执行时机控制粒度和返回值约定。
- MySQL 的
BEforE触发器能修改NEW行数据(比如自动填充created_at),AFTER则不能;PostgreSQL 的BEFORE同样可改NEW,但必须显式RETURN NEW,否则插入失败 - PostgreSQL 支持
FOR EACH STATEMENT和FOR EACH ROW,MySQL 只支持行级;批量更新时,语句级触发器能避免 N 次重复开销 - MySQL 不允许在触发器里调用存储函数以外的外部服务(比如 http 请求),PostgreSQL 虽可通过
curl扩展实现,但会严重拖慢事务,实际没人这么干
示例(PostgreSQL 自动更新修改时间):
CREATE OR REPLACE FUNCTION update_modified_column() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ language 'plpgsql'; <p>CREATE TRIGGER update_users_modtime BEFORE UPDATE ON users FOR EACH ROW EXECUTE FUNCTION update_modified_column();
哪些错误现象说明你滥用触发器了?
不是语法报错,而是线上行为开始“不对劲”。
- 应用层执行一条
UPDATE,数据库 CPU 突然飙升 5 秒,查pg_stat_activity发现一堆idle in transaction—— 很可能是触发器里写了低效循环或未索引的子查询 - 同一个
user_id在日志表里出现两条时间戳完全一致的记录,但业务上不可能并发改同一个人 —— 触发器没加IF TG_OP = 'UPDATE'判断,INSERT也误触发了 - 导出数据后重新导入,触发器反复执行导致计数字段翻倍(比如
total_orders加了两次)—— 忘了关掉LOAD DATA或copy期间的触发器,也没用DISABLE TRIGGER
替代触发器的更可控方案有哪些?
把逻辑提到应用层或数据库外,不是为了“去掉触发器”,而是把控制权拿回来。
- 用 ORM 的钩子(如 django 的
save()、laravel 的booted)替代简单字段填充,便于单元测试和打点监控 - 对强一致性要求高的场景(如库存扣减),用
SELECT ... FOR UPDATE+ 显式事务,比靠触发器“事后补救”更可靠 - 审计类需求(谁什么时候改了什么),优先考虑数据库自带的
pg_audit(PostgreSQL)或general_log(MySQL),而不是自己写触发器记日志——前者不参与事务,不影响性能,且格式统一
触发器真正的价值,是兜底。它不该是你第一个想到的方案,而应是你试过所有更透明、更易测、更易维护的方式之后,还不得不留的一道窄门。门后那点逻辑,越薄越好。