MySQL存储过程不能自动定时执行,需依赖事件调度器或外部调度器;启用事件调度器需SET GLOBAL event_scheduler = ON;创建事件须指定未来STARTS时间;大表归档必须分批处理以避免锁表和超时。

直接结论:MySQL存储过程本身不能“定时”,必须配合事件调度器(Event Scheduler)或外部调度器(如cron、Spring @Scheduled)才能实现自动归档;单纯写个存储过程不调用,它就只是个函数,不会自己跑。
MySQL事件调度器(Event Scheduler)怎么启用并创建归档事件
这是最轻量的内置方案,适合测试环境或低频小数据归档。但生产环境慎用——事件无法捕获执行失败、不支持重试、日志难追踪。
- 先确认并开启调度器:
SET GLOBAL event_scheduler = ON;(需SUPER权限) - 事件必须显式指定数据库名,例如在
myapp库中创建:CREATE EVENT archive_orders_daily ON SCHEDULE EVERY 1 DAY STARTS '2026-05-09 02:00:00' DO CALL archive_orders('2026-05-08'); - 注意:
STARTS时间必须是未来时间,否则事件创建后状态为SLAVESIDE_DISABLED,且不会自动恢复 - 事件默认在定义它的数据库上下文中执行,所以
CALL archive_orders(...)里的表名无需加库前缀,但跨库操作必须显式写成archive_db.orders_archive
存储过程里为什么必须分批处理,而不是一次INSERT+DELETE
大表一次性迁移容易锁表、拖慢业务、触发max_allowed_packet或事务超时。尤其当原表有主键自增、唯一索引或外键时,全量操作风险更高。
- 推荐用
INSERT INTO ... SELECT ... LIMIT 1000+DELETE ... LIMIT 1000循环,每次只处理固定行数 - 循环控制建议用
ROW_COUNT()判断是否还有数据可处理,避免死循环:REPEAT INSERT INTO orders_archive SELECT * FROM orders WHERE order_date
- 不要依赖
SELECT COUNT(*)预估总行数再循环——COUNT可能不准(MVCC快照)、且多一次全表扫描 - 务必在
WHERE条件字段(如order_date)上建索引,否则LIMIT无效,实际还是全表扫描
Java调用存储过程归档时,Connection和CallableStatement怎么配才不翻车
Spring Boot项目里用JdbcTemplate或原生JDBC调用,最容易出问题的是连接生命周期和事务边界。
- 别在
@Scheduled方法里手动new Connection()——Spring管理的DataSource已含连接池,应复用它 - 用
JdbcTemplate.update("CALL archive_orders(?)", cutoffDate)即可,底层自动处理CallableStatement参数绑定和关闭 - 如果要捕获归档影响行数,改用
queryForObject("SELECT ROW_COUNT()", Integer.class),但注意:该值是上一条语句(即CALL内部最后一条DML)的结果,不是整个存储过程的累计值 - 归档逻辑涉及多张表或需强一致性时,必须加
@Transactional,否则INSERT成功但DELETE失败会导致数据不一致 - 别把
cutoff_date拼成字符串传入,用LocalDate或java.sql.Date类型参数,避免时区/格式歧义
归档表结构没同步更新,存储过程执行就报错
这是线上最常被忽略的坑:原表加了字段、改了类型、删了索引,但归档表忘了同步,INSERT INTO ... SELECT *直接失败。
稳妥做法不是靠人工比对,而是在存储过程开头加校验逻辑:
SELECT COUNT(*) INTO @src_cols FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'orders'; <p>SELECT COUNT(*) INTO @arc_cols FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'orders_archive';</p><p>IF @src_cols != @arc_cols THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '归档表结构不匹配'; END IF;</p>
更进一步,可动态生成CREATE TABLE ... LIKE语句重建归档表(需CREATE权限),但要注意:重建会丢失归档表已有数据,仅适用于首次初始化或允许清空归档库的场景。











