不建议用触发器自动维护复杂报表系统的中间汇总表。它看似实时,实则在并发、批量、历史补数、锁竞争等真实场景下极易失效,甚至拖垮主业务。

不建议用触发器自动维护复杂报表系统的中间汇总表。它看似实时,实则在并发、批量、历史补数、锁竞争等真实场景下极易失效,甚至拖垮主业务。
为什么触发器在复杂汇总场景下会卡死或出错
触发器运行在原始事务上下文中,所有写操作共享同一把锁。比如你在 AFTER INSERT 里去 UPDATE fact_daily_summary,而这张表正被另一个并发任务更新同一日期的行,就会立刻出现 LCK_M_U 或 LCK_M_SCH_S 等等待;若该汇总表本身还有触发器,还可能触发嵌套调用,SQL Server 默认只允许 32 层,超限直接报 Msg 217。
MySQL 更严格:BEFORE 触发器读不到新插入的行(NEW 可用但表中不可见),AFTER 又不能改 NEW 值;想用 IF NOT EXISTS 判断再插入,高并发下必现 ERROR 1062: Duplicate entry。
- 触发器无法处理 TRUNCATE —— 它不触发任何事件,总数直接失准
- 无法回滚部分失败:比如主表插入成功、汇总表更新失败,整个事务会回滚,但业务可能只想要“尽力而为”
- 批量导入(如
LOAD DATA INFILE或 SSIS)可能绕过触发器,或让其执行 N 次导致重复计数
如果非要用,至少避开这三类致命写法
真要上触发器,必须满足三个前提:汇总字段是原子增减(非聚合)、目标表无触发器、有唯一约束兜底。
- 别在触发器里写
SELECT COUNT(*) FROM main_table—— MySQL 直接报错,SQL Server 扫全表拖慢插入 - 别用
INSERT ... SELECT或REPLACE INTO做 upsert,必须用带唯一键的INSERT ... ON DUPLICATE KEY UPDATE(MySQL)或MERGE(SQL Server) - UPDATE 场景必须覆盖 status 变更:比如只统计
status = 'active'的行,那AFTER UPDATE就得判断OLD.status != NEW.status,否则数据悄悄漂移
真正可用的替代路径:定时任务 + 存储过程
把统计逻辑从实时链路里摘出来,才能兼顾正确性、可观测性和运维弹性。
- SQL Server:用 SQL Server Agent 创建作业,调用
usp_UpdateDailySummary,WHERE 条件限定为uploadTime >= DATEADD(minute, -5, GETDATE()),避免重复扫描 - MySQL:先开
SET GLOBAL event_scheduler = ON,再建event_update_daily_report,频率设EVERY 5 MINUTE - 关键不是“多快”,而是“可重试”:存储过程中加
TRY...CATCH或DECLARE EXIT HANDLER,失败写日志表,人工介入后手动补跑
最常被忽略的一点:触发器没法区分“新增一行”和“修正一行”。比如订单金额被运营后台修改,你该加总还是减总?业务语义必须由外部系统明确传入标记(如 is_correction = 1),而触发器本身没有上下文能力去推断——这事只能靠解耦后的定时任务,在完整事务中处理清楚。











