触发器不能自动同步老数据到归档表,仅响应单行变更;按时间归档需EVENT或应用层定时任务;其安全用途限于标记待归档行或保存快照。

不能直接用触发器“自动同步老数据到归档表”——它只响应单行变更,不扫描条件、不批量迁移、不主动执行。所谓“按时间归档90天前订单”,必须靠 EVENT 或应用层定时任务;触发器唯一安全角色是标记待归档行或保存变更快照。
BEFORE DELETE 触发器只能归档被删的那一行
DELETE 语句删多少行,触发器就执行多少次。比如 DELETE FROM orders WHERE created_at 删除 5000 行,<code>BEFORE DELETE 就会插入 5000 次归档表——不是“一次归档”,而是 5000 次独立事务内写入,极易锁表、拖慢主业务。
- 必须用
BEFORE DELETE:AFTER 中OLD字段已不可靠,尤其含TEXT、JSON或开启innodb_strict_mode时可能为NULL - 归档表结构必须和主表严格一致:字段名、类型、长度、是否允许
NULL、字符集、排序规则,否则插入静默截断(例如主表TEXT→ 归档表VARCHAR(255)) - 主键处理要小心:归档表不能复用原表
PRIMARY KEY,否则同一id多次删除会报Duplicate entry;建议加自增archive_id,原id改为普通索引
BEFORE UPDATE 触发器只存最新变更前的快照
它不会自动归档历史版本,你必须显式写 INSERT INTO archive_table SELECT OLD.*,漏掉这句就等于什么都没做。而且每次更新都触发一次归档,不管改的是不是关键字段。
-
OLD.*只在BEFORE UPDATE和BEFORE DELETE中完整可用;AFTER触发器里OLD已失效 - 归档动作和原
UPDATE在同一事务中:主表行锁持有时间延长,高并发下易堆积锁等待 - 归档表
auto_increment可能成热点,尤其当更新频率远高于查询频率时 - 大字段(如
TEXT、JSON)会被整行复制,浪费 I/O 和存储,且无法控制归档粒度
为什么别在触发器里调存储过程做归档
看似封装了逻辑,实则放大风险:存储过程若含 SELECT ... FROM main_table 或跨库写入,会在主表写入事务中引发全表扫描、死锁、权限失败或 binlog 异常。
- 触发器以当前连接用户权限运行,即使
DEFINER是 root,调用存储过程仍需该用户有EXECUTE权限,否则静默失败 -
EVENT默认也以DEFINER用户运行,若该用户对归档表无INSERT权限,归档会失败但LAST_ERROR可能为空——务必查information_schema.EVENTS - 真正可控的做法:触发器只往轻量队列表(如
archive_queue)插一行,含table_name、pk_id、archived_at;后续由 Python 脚本分批消费,带重试、限流、校验
归档失败不会回滚主操作,必须手动兜底
MySQL 触发器里 INSERT 归档失败,默认不会中断主 DELETE 或 UPDATE,结果就是“数据丢了,归档没做”,静默丢数据。
- 必须显式检查:用
IF ROW_COUNT() = 0 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Archive insert failed'; END IF; - PostgreSQL 更可靠:函数返回
NULL可中止主操作,但前提是声明RETURNS trigger并正确返回OLD - 最易被忽略的一点:归档表字段定义与主表的毫秒精度差异(如主表
TIMESTAMP(6)→ 归档表DATETIME)会导致时间被截断为0000-00-00 00:00:00,而日志看起来“成功”


















