MySQL触发器不能定时执行,仅响应INSERT、UPDATE、DELETE操作;定时归档需用EVENT或应用层任务,且须分批处理、建索引、更新统计信息并谨慎维护表空间。

MySQL触发器不能定时执行,别被名字骗了
触发器(TRIGGER)只响应 INSERT、UPDATE、DELETE 这三类语句,压根没有“定时”能力。所谓“自动归档老数据”的定时需求,必须靠外部机制驱动,比如 EVENT(事件调度器)或应用层定时任务。直接在触发器里写 SLEEP() 或试图模拟定时逻辑,不仅无效,还会阻塞事务、拖慢主表写入。
-
EVENT是 MySQL 原生支持的定时机制,需确认event_scheduler已启用:SHOW VARIABLES LIKE 'event_scheduler';,返回ON才可用 - 触发器适合做“联动反应”,比如某条订单状态变
'completed'后立即挪进归档表;但判断“是否满30天”这种跨行、跨时间条件,放触发器里既难维护又易出错 - 如果误把归档逻辑全塞进
BEFORE DELETE触发器,可能因归档失败导致原删除操作回滚,反而卡住业务
归档逻辑该放 EVENT 还是应用层
取决于数据量、一致性要求和运维控制粒度。小规模(日增<1万行)、允许分钟级延迟的场景,用 EVENT 最省事;高一致性或需复杂判断(如归档前校验关联表状态)时,应用层更可控。
-
EVENT示例:每小时归档一次orders表中created_at < DATE_SUB(NOW(), INTERVAL 90 DAY)的记录 - 应用层优势:可加重试、限流、打点监控;能统一处理多库/分表归档;避免数据库长期持有大事务锁
- 容易踩的坑:
EVENT默认在DEFINER用户权限下运行,若该用户无目标表写权限,归档会静默失败——务必用SHOW EVENTS;查状态,再用SELECT * FROM information_schema.EVENTS;看最后执行错误
归档 SQL 性能关键:别直接 DELETE + INSERT
一次性搬几百万行?那不是归档,是给数据库喂炸弹。必须分批操作,且避免全表扫描。
- 用主键范围分片:
WHERE id BETWEEN ? AND ? AND created_at < ...,比LIMIT更稳定(避免跳过数据) - 归档前先
CREATE INDEX加速查询条件,比如created_at和状态字段的联合索引 - 别在归档语句里用子查询判断是否存在:
INSERT ... SELECT ... WHERE NOT EXISTS (SELECT 1 FROM archive_table ...),这会让每次插入都扫一遍归档表——改用INSERT IGNORE或ON DUPLICATE KEY UPDATE(需提前建好唯一键) - 示例分批归档语句:
INSERT INTO orders_archive SELECT * FROM orders WHERE id >= 100000 AND id < 101000 AND created_at < DATE_SUB(NOW(), INTERVAL 90 DAY); DELETE FROM orders WHERE id >= 100000 AND id < 101000 AND created_at < DATE_SUB(NOW(), INTERVAL 90 DAY);
归档后记得更新统计信息和清理碎片
MySQL 不会自动感知大块数据迁移后的索引分布变化,尤其 MyISAM 或未开启 innodb_stats_persistent 的 InnoDB 表,查询计划可能劣化。
- 执行
ANALYZE TABLE orders;强制更新统计信息(InnoDB 下代价低,建议归档后立刻跑) - 若用
DELETE删除大量数据,InnoDB 表空间不会自动收缩——需要OPTIMIZE TABLE orders;,但该操作会锁表,生产环境慎用;更稳妥的是导出+重建,或等 MySQL 8.0+ 的ALTER TABLE ... REBUILD - 归档表本身也得定期维护:
ARCHIVE引擎虽省空间,但不支持索引,查起来慢;InnoDB归档表建议单独建created_at分区,按月自动落盘
归档这事,核心矛盾从来不是“怎么写SQL”,而是“什么时候动、动多少、动完怎么收场”。时间窗口、锁粒度、监控断点,这些比语法细节更容易让整个流程崩在上线后。

















