订单状态一改,审计表里自动多一条记录,翻遍业务代码也没找到对应的 insert。问了一圈,是数据库里的触发器在干活,表名和触发时机都没在文档里写。
sql 触发器是绑在表上的特殊存储过程,INSERT、UPDATE、DELETE 发生时自动执行。它和普通存储过程的区别是调用方式:一个手动执行,一个由数据变更触发。
一个最小可用的例子
MySQL 里可以这样写,更新订单后把新旧状态记进日志表,单条语句不需要改分隔符,多条语句才要。
CREATE TRIGGER trg_order_update AFTER UPDATE ON orders FOR EACH ROW INSERT INTO order_log(order_id, old_status, new_status) VALUES (OLD.id, OLD.status, NEW.status);OLD 表示修改前的行,NEW 表示修改后的行,删除只有 OLD,插入只有 NEW。SQL Server 的写法不一样,用 inserted 和 deleted 两张虚拟表,按语句触发,批量更新时里面可能是多行。SQL Server 的触发器没有 BEFORE,想校验得用 INSTEAD OF。
适合做审计,不适合塞业务
记日志、维护汇总字段、做简单的数据校验,放触发器里很省事,应用层漏写也会被执行。但订单流转、库存扣减这类核心逻辑塞进去,排查问题时很难找,改一次还要动数据库。
触发器里的报错多数情况会让整个事务回滚,这点常被忽略。嵌套触发器和递归触发器的开关也要留意,SQL Server 默认允许嵌套,层数有上限,出事时日志里能看到。MySQL 里同一个表、同一时机、同一事件,新版本允许多个触发器,老版本只允许一个。
上线前先查一遍现有触发器
MySQL 用 SHOW TRIGGERS,或者查 information_schema.triggers;SQL Server 用 sp_helptrigger 加表名。大表上的触发器会拖慢批量更新,导入数据前先禁用,完事再启用。sql 触发器能不用就不用,能放在应用层事务里的逻辑,尽量别藏进数据库,先在测试库造一批数据跑一遍,比上线后回滚便宜。