触发器不适合做历史数据归档,因其仅响应INSERT/UPDATE/DELETE事件,无法定时执行;依赖业务操作触发会导致归档不全、性能下降,且无法满足定期全量扫描归档需求。

不能直接用触发器自动归档两年前的数据——触发器只响应 INSERT/UPDATE/DELETE 操作,不支持定时调度。
为什么触发器不适合做历史数据归档
触发器本质是事件驱动的,比如 INSERT 时执行一段逻辑。它不会在“每年1月1日零点”或“每天凌晨2点”自动跑一次。想靠 BEFORE INSERT ON orders 或 AFTER DELETE ON orders 来清理两年前的数据,既无法覆盖全量,也容易拖慢业务写入。
- 归档需要扫描全表(
SELECT * FROM orders WHERE created_at ),而触发器里执行这类语句会严重阻塞主表操作 - MySQL 不允许在触发器中对触发它的表做
INSERT/UPDATE/DELETE(会报错ERROR 1442) - PostgreSQL 虽然允许,但递归触发、事务可见性、锁竞争问题会让逻辑极难稳定
真正可行的归档方案:用事件 + 存储过程(MySQL)
MySQL 5.7+ 支持 EVENT,可设置为每天执行一次归档任务。需先开启事件调度器:SET GLOBAL event_scheduler = ON;
- 创建归档表结构(字段一致,可加
archived_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP):CREATE TABLE orders_archive LIKE orders; ALTER TABLE orders_archive ADD COLUMN archived_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
- 写存储过程,带事务和行数限制(防锁表太久):
DELIMITER $$ CREATE PROCEDURE archive_old_orders() BEGIN DECLARE row_count INT DEFAULT 1; WHILE row_count > 0 DO START TRANSACTION; INSERT INTO orders_archive SELECT *, NOW() FROM orders WHERE created_at < DATE_SUB(NOW(), INTERVAL 2 YEAR) LIMIT 1000; DELETE FROM orders WHERE created_at < DATE_SUB(NOW(), INTERVAL 2 YEAR) LIMIT 1000; SET row_count = ROW_COUNT(); COMMIT; END WHILE; END$$ DELIMITER ; - 创建每日事件:
CREATE EVENT daily_archive_orders ON SCHEDULE EVERY 1 DAY STARTS '2025-01-01 02:00:00' DO CALL archive_old_orders();
PostgreSQL 怎么办:用 pg_cron 扩展
PostgreSQL 原生无事件调度,但 pg_cron 是官方推荐的扩展(需管理员安装)。启用后,直接用 SQL 注册定时任务:
- 确保已加载:
CREATE EXTENSION IF NOT EXISTS pg_cron; - 添加每日归档作业:
SELECT cron.schedule( 'daily-archive-orders', '0 2 * * *', -- 每天凌晨2点 $$WITH old AS ( SELECT * FROM orders WHERE created_at < NOW() - INTERVAL '2 years' LIMIT 1000 ) INSERT INTO orders_archive SELECT *, NOW() FROM old; DELETE FROM orders WHERE id IN (SELECT id FROM old);$$ ); - 注意:必须用
id或其他唯一键做IN删除,避免WHERE created_at 在并发下漏删或多删(时间字段可能不唯一)
归档前必须检查的三个坑
跳过这些,归档过程大概率失败或丢数据:
-
created_at字段是否建了索引?没索引的WHERE created_at 会全表扫描,归档变卡死 - 归档表和原表的字符集、排序规则、外键约束是否一致?不一致会导致
INSERT ... SELECT报错ERROR 1267 - 归档过程是否避开业务高峰?即使分批删除,也要监控
innodb_row_lock_time_avg(MySQL)或pg_locks(PG),防止阻塞订单写入
归档不是设好就完事的事——两年这个阈值一旦写死,后续业务要查“近26个月”数据就会跨表;更麻烦的是,归档后应用层查询若没改 SQL,会直接查不到数据。这事得数据库、应用、运维一起对齐边界。

















