不能,触发器不适合做归档迁移;它在dml执行中同步触发,归档逻辑变慢或出错会拖垮业务写入,且无法可靠处理批量操作。

触发器能直接做归档迁移吗?不能,别这么干
触发器本身不适合承担数据归档任务。它在 INSERT/UPDATE/DELETE 语句执行过程中同步触发,一旦归档逻辑(比如插入到历史表、清理原表)变慢或出错,会直接拖垮业务写入——用户看到的是超时或死锁,而不是“归档失败”。更危险的是,触发器无法可靠处理批量操作(如 DELETE FROM orders WHERE created_at ),容易漏迁、重复迁或事务卡死。
真正可用的归档方案:用存储过程 + 定时任务
把归档逻辑封装成带事务控制和错误检查的存储过程,再由外部调度驱动,才是生产可行路径。以 PostgreSQL 为例:
CREATE OR REPLACE PROCEDURE archive_old_orders(archive_before DATE) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO orders_history SELECT * FROM orders WHERE created_at DELETE FROM orders WHERE created_at <p>-- 关键:确保两步原子性或可重试 IF NOT FOUND THEN RAISE NOTICE 'No rows archived for %', archive_before; END IF; END; $$;</p>
-
archive_old_orders()必须显式传入时间边界,避免硬编码导致误删 - 务必在
DELETE后检查FOUND或行数,防止误判空归档为成功 - MySQL 用户注意:
PROCEDURE不支持事务自动回滚,需手动加START TRANSACTION和ROLLBACK - 归档前建议先
ANALYZE orders(PostgreSQL)或更新统计信息(MySQL),避免执行计划退化
定时调度选什么?pg_cron 比 systemd timer 更稳
数据库内调度比 OS 层更可靠:避免因服务器重启、脚本权限、路径变更导致归档中断。PostgreSQL 推荐直接装扩展 pg_cron:
SELECT cron.schedule('0 2 * * *', $$
CALL archive_old_orders('2024-01-01'::DATE)
$$);
-
cron.schedule()返回 job_id,可用于后续cron.unschedule() - 时间表达式必须是 UTC,别按本地时区写,否则跨夏令时会错乱
- MySQL 没有内置方案,只能用
mysqld_safe --syslog+ 外部crond调用mysql -e "CALL ...",但失败不告警、无日志追踪,运维成本高 - 所有调度任务必须配
LOG行为:例如在存储过程开头写INSERT INTO archive_log ...,否则故障时完全无法定位
归档后索引和统计怎么不崩?必须重建
大批量 DELETE 后,B-tree 索引不会自动收缩,查询性能断崖下跌;统计信息过期会导致优化器选错执行计划。这不是“可能”,而是必然发生:
- PostgreSQL:归档后立即跑
VACUUM ANALYZE orders,大表可加VERBOSE看膨胀率 - MySQL(InnoDB):
OPTIMIZE TABLE orders会重建聚簇索引,但会锁表;更安全用ALTER TABLE orders ENGINE=InnoDB(8.0+ 支持在线) - 千万别省略对历史表的索引维护:
orders_history上要建created_at分区或 BRIN 索引(PG),否则查历史数据一样慢 - 归档窗口别设太宽,单次迁移超 100 万行就拆成多批次,配合
WHERE id BETWEEN x AND y避免长事务
归档不是“写个触发器就完事”,核心在于把数据搬移从实时链路里剥离开,再补上可观测、可中断、可验证的运维闭环。最容易被跳过的其实是日志记录和索引重建——等查不出数据或响应变慢了,才想起翻日志,那时已经晚了。










