mysql 8.0 中 event_scheduler 必须启用才能执行事件,普通用户无权执行 set global event_scheduler = on,需 dba 在 my.cnf 中配置 event_scheduler = on 并重启;event 权限须显式授予具体数据库(如 grant event on app_log.*),且 create event 必须带 enable 关键字,否则默认 disable。

MySQL 8.0 的存储过程本身不会自动运行,必须靠事件调度器(Event Scheduler)或外部调度器触发;直接写好存储过程却不调用,它就只是个“待命状态”的代码块。
怎么确认并启用 event_scheduler
这是所有定时清理的前提,90% 的失败都卡在这一步。即使 CREATE EVENT 语法完全正确,只要调度器关着,事件就创建失败或静默不执行——错误提示常是误导性的 ERROR 1547: Event execution time is in the past。
- 查状态:
SHOW VARIABLES LIKE 'event_scheduler';—— 返回OFF就必须开 - 临时启用(重启失效):
SET GLOBAL event_scheduler = ON;(需SUPER权限) - 永久启用(生产环境必须):在
my.cnf的[mysqld]段加event_scheduler = ON,然后重启 MySQL - 云数据库(如阿里云 RDS、腾讯云 CDB)通常禁用该变量,此时只能放弃
EVENT,改用crontab或 Spring Boot 的@Scheduled调用CALL clean_archive_data();
为什么 cleanup 存储过程必须分批 DELETE + LIMIT
归档表动辄百万行,DELETE FROM archive_log WHERE create_time 这种语句若不加限制,MySQL 会一次性扫描+锁住所有匹配行,极易触发 <code>Lock wait timeout exceeded,阻塞业务写入甚至拖垮主库。
- 必须用循环 +
DELETE ... LIMIT 1000,每次只删固定行数 - 每次删完建议加
DO SLEEP(0.1);,让出资源,避免连续高负载 - 判断是否继续用
ROW_COUNT(),而不是先SELECT COUNT(*)—— 后者不准(MVCC 快照)、且多一次全表扫描 -
create_time字段必须有索引,否则LIMIT也救不了全表扫描
CREATE EVENT 里别直接写 DELETE,要用 CALL 存储过程
把清理逻辑硬塞进 CREATE EVENT ... DO DELETE ... 是高危操作:WHERE 条件写错、索引失效、没分批,可能一夜间清空整张归档表。存储过程才是可控的“安全沙箱”。
- 事件只负责“到点唤起”,比如每天凌晨 2 点执行:
CREATE EVENT clean_archive_daily ON SCHEDULE EVERY 1 DAY STARTS '2026-09-05 02:00:00' DO CALL clean_archive_data(); - STARTS 时间必须是未来时间,否则事件状态为
SLAVESIDE_DISABLED且不会自恢复 - 事件默认在定义它的数据库上下文中执行,所以存储过程内表名无需加库前缀;跨库操作则必须显式写成
archive_db.log_archive - 别信
NOW():它返回语句开始执行时的时间戳;在事件中用CURRENT_TIMESTAMP更可靠,它是实时求值的
归档表清理后要不要 OPTIMIZE TABLE
对 InnoDB 表,OPTIMIZE TABLE 实质是重建表+释放碎片空间,但会锁表、耗 IO,线上慎用。
- 仅当归档表频繁大量删除(比如每天删 50 万行以上),且观察到
data_free显著增长、查询变慢时才考虑 - 执行前务必确认业务低峰期,并评估锁表影响;可改用
ALTER TABLE archive_log ENGINE=InnoDB;达到相同效果 - 如果用了分区表(如按月分区),优先用
ALTER TABLE archive_log DROP PARTITION p202506;,比 DELETE + OPTIMIZE 快得多、也安全得多
最易被忽略的是:事件创建后不会自动校验存储过程中涉及的表是否存在、字段是否可读、权限是否足够——这些错误只有真正执行到那一步才会暴露,且无日志可查。上线前务必手动 CALL clean_archive_data(); 测试一遍,并检查 SELECT * FROM information_schema.EVENTS WHERE EVENT_NAME = 'clean_archive_daily'; 确认状态是 ENABLED 且 LAST_EXECUTED 有更新。











