在业务系统里,当主表发生写入或变更时,关联表往往也要跟着调整。如果每次都在应用代码里显式去更新,不仅逻辑分散,还容易因为异常分支导致数据不一致。MySQL提供的触发器对象,允许我们把这部分联动逻辑下沉到数据库层,由引擎在指定事件发生时自动调用。它绑定在具体的表上,监听insert、update或delete,并能在操作前或操作后执行一段SQL语句体。

触发器的核心概念与执行时机
触发器本质上是一种特殊的存储程序,它和表紧密关联,无法像普通函数那样被直接调用。MySQL支持六种组合:before insert、after insert、before update、after update、before delete、after delete。这里的before表示在真正修改数据之前执行,如果触发器里抛出了错误,原操作会被中断;after则是在数据已经落盘之后执行,此时再去查刚刚改动过的行是安全的,但无法阻止本次修改。
理解执行时机非常关键。比如你要校验用户余额不能为负,就必须在before update里检查并signal报错;而如果你要把变更记录写进审计日志表,放在after里更合适,因为这时主表行已经确定写入成功,不会出现日志有记录但主表没改动的错位情况。另外需要注意,MySQL每张表每种事件只允许有一个before和一个after触发器,不能叠加多个同名时机触发器。
触发器内部可以通过old和new两个伪记录来访问数据。对于insert,只有new有效,代表即将插入的行;对于delete,只有old有效,代表将被删除的行;update则old是修改前的值,new是修改后的值。在before update中,你甚至可以直接给new.column赋值,从而悄悄改变最终写进去的内容,这也是很多自动化填充字段技巧的实现基础。
创建触发器的完整语法与示例
创建触发器使用create trigger语句,基本结构如下。先指定触发器名字,再写before或after加事件类型,on后面跟表名,for each row表示行级触发,MySQL只支持行级不支持语句级。接着begin和end之间写具体逻辑,如果逻辑只有一条sql可以省略begin end。
create trigger trg_after_order_insert
after insert on orders
for each row
begin
insert into order_stat(user_id, total_amount, order_count)
values (new.user_id, new.amount, 1)
on duplicate key update
total_amount = total_amount + new.amount,
order_count = order_count + 1;
end;
上面例子演示了订单表插入后,自动把金额和笔数累加到统计表。这里用到了on duplicate key update,因为统计表可能已存在该用户的汇总行。new.user_id和new.amount就是刚插入订单的值。如果业务还要求删除订单时回滚统计,可以再建一个after delete触发器,用old里的值做减法。
删除触发器用drop trigger,如果触发器名在其它库需指定库名。查看已有触发器可执行show triggers,或者查询information_schema.triggers表。建议在命名上带上表名和时机,例如trg_表名_事件_时机,这样后期维护能一眼看出作用范围。同时触发器里的sql也应当尽量简单,避免写复杂循环,否则会让原操作的延迟明显上升。
实际应用场景与常见避坑点
除了前面说的统计汇总,触发器还常用于软删除标记同步、跨表冗余字段更新、以及敏感操作留痕。比如用户表status变成禁用时,用before update触发器把该用户所有token表的expire_time提前,就能在数据库层强制下线,不依赖业务代码补刀。这种方案的好处是无论哪条路径改了用户状态,副作用都会生效。
但使用触发器也有坑。其一是递归触发和级联触发:如果触发器A改了表B,而表B也有触发器又回头改表A,可能形成死循环,MySQL默认不阻止这种行为,需要靠设计避免。其二是权限问题,触发器以定义者的权限运行,如果定义者账号被回收了相关表的权限,触发器执行会报错,而不是以调用者权限跑。其三是调试困难,触发器逻辑藏在数据库里,应用层日志看不到,出问题时要专门去查数据库端。
还有一个常见误区是拿触发器当事务替代品。虽然触发器在包含原操作的同一个事务里运行,但如果触发器里调用了外部存储过程做跨库操作,或者写了非事务引擎的myisam表,就无法保证原子性。因此重要的一致性逻辑,仍要结合应用层事务或消息队列做兜底。合理评估哪些联动适合下沉,才能让触发器真正提升效率而不是埋雷。