mysql 5.7+ 或 postgresql 12+ 才能较可靠实现每日自动归档+删除;旧版本不支持 event 持久调度或 range 分区,强行用脚本轮询易漏执行、重复归档或事务卡死。

归档前先确认数据库是否支持分区表或事件调度器
MySQL 5.7+ 或 PostgreSQL 12+ 才能较可靠地实现“每日自动归档+删除”,旧版本(如 MySQL 5.6)不支持 EVENT 持久调度,或缺乏 PARTITION BY RANGE (TO_DAYS()) 等能力。强行用脚本轮询或 crontab 调存储过程,容易漏执行、重复归档、事务卡死。
实操建议:
- MySQL 用户优先查
SHOW VARIABLES LIKE 'event_scheduler';,必须为ON;再确认表已按日期字段(如created_at)做了RANGE COLUMNS分区 - PostgreSQL 用户检查是否启用了
pg_cron扩展:SELECT * FROM pg_extension WHERE extname = 'pg_cron'; - SQL Server 用户需依赖
SQL Server Agent,不能只靠 T-SQL 定时逻辑
用 INSERT … SELECT + DELETE 做原子归档(非分区场景)
如果无法用分区或调度器,就得手动写归档逻辑——核心是保证归档与删除在同一个事务里完成,避免中间状态丢失数据。
常见错误现象:归档成功但删除失败,导致重复归档;或先删后插,造成数据真空。
正确做法(以 MySQL 为例):
BEGIN; INSERT INTO archive_orders SELECT * FROM orders WHERE created_at <p>注意点:</p>
- 务必给
created_at加索引,否则DELETE会全表扫描,锁表时间长 - 不要用
SELECT *归档到结构不同的表,字段顺序/类型不一致会静默截断或报错Truncated incorrect DOUBLE value - 单次处理量大时(如 >10 万行),拆成批次:
WHERE id BETWEEN ? AND ? AND created_at ,避免事务日志暴涨
MySQL EVENT 自动触发归档的坑:时间表达式不能用 NOW()
很多人写 CREATE EVENT daily_archive ON SCHEDULE EVERY 1 DAY DO ... 后发现归档日期总错一天——因为 NOW() 在事件定义时被固化,不是每次执行时求值。
正确写法必须用函数包裹动态时间:
CREATE EVENT daily_archive ON SCHEDULE EVERY 1 DAY DO INSERT INTO archive_orders SELECT * FROM orders WHERE created_at <p>关键区别:</p>
-
CURDATE()每次触发都重新计算,NOW()在CREATE EVENT语句解析时就固定了 - 事件默认用
SYSTEM时区,若服务器和业务时区不一致,得显式设:ON SCHEDULE EVERY 1 DAY STARTS '2024-01-01 02:00:00' + INTERVAL 0 HOUR - 事件不会自动重试失败任务,出错后状态变成
SLAVESIDE_DISABLED,需手动ALTER EVENT daily_archive ENABLE;
归档后立即 ANALYZE TABLE 很重要
归档操作大幅改变主表行数分布,优化器统计信息滞后会导致后续查询走错索引,比如原本该走 created_at 索引的 WHERE 条件,突然变全表扫描。
别省这一步:
- MySQL:归档
DELETE后立刻执行ANALYZE TABLE orders; - PostgreSQL:对应
ANALYZE orders;,且建议配合VACUUM回收空间 - SQL Server:执行
UPDATE STATISTICS orders;,尤其当归档量超表总行数 10% 时
这个动作常被跳过,结果是归档完成了,线上查询却慢了三倍——问题不在归档逻辑本身,而在统计信息没更新。










