MySQL 5.7需启用event_scheduler才能使用EVENT,否则CREATE EVENT失败;云数据库常禁用该功能,须改用外部crond调用带LIMIT和SLEEP的存储过程清理数据。

MySQL 5.7 本身不支持存储过程“自动定时”,必须靠 Event Scheduler 触发;但直接在 EVENT 里写 DELETE 是高危操作,必须用存储过程封装清理逻辑。
event_scheduler 没开,CREATE EVENT 一定失败
这是最常卡住人的第一步:语法完全正确,却报 ERROR 1547 或 ERROR 1541: Event scheduler is disabled。根本原因不是 SQL 写错,而是调度器压根没启动。
- 查状态:
SHOW VARIABLES LIKE 'event_scheduler';—— 返回OFF就得开 - 临时启用(重启失效):
SET GLOBAL event_scheduler = ON; - 永久启用:在
my.cnf的[mysqld]段加event_scheduler = ON,然后重启 MySQL - 云数据库(如阿里云 RDS、腾讯云 CDB)通常禁用该变量,此时 EVENT 不可用,只能改用外部脚本 + crond 调用
CALL clean_old_logs();
存储过程里 DELETE 必须带 LIMIT 和 SLEEP
日志表或缓存表动辄百万行,DELETE FROM log_table WHERE created_at 这种语句若不加控制,MySQL 会一次性锁住所有匹配行,极易触发 <code>Lock wait timeout exceeded,阻塞业务写入。
- 必须分批删:用
WHILE循环 +DELETE ... LIMIT 10000 - 每次删完加
DO SLEEP(0.1),让出资源,避免拖垮线上服务 -
created_at字段必须有索引,否则LIMIT也救不了全表扫描 - 别用
TRUNCATE:它不能带条件,也不走事务,无法按时间范围清理
EVENT 的 STARTS 时间要用 CURRENT_TIMESTAMP,不是 NOW()
在 EVENT 定义中写 STARTS NOW() + INTERVAL 1 HOUR 是危险的——NOW() 返回的是语句开始执行时的时间戳,不会随事件实际触发时间动态更新;而 CURRENT_TIMESTAMP 在 EVENT 内部会被实时求值,更可靠。
- 正确写法示例:
STARTS CURRENT_TIMESTAMP + INTERVAL 1 HOUR - 周期性事件推荐用固定时间点,比如:
EVERY 1 DAY STARTS '2026-05-10 03:00:00',避开业务高峰 - 云环境若禁用 EVENT,就只能把
CALL proc_clean_90days()塞进 shell 脚本,用系统 crond 每天调一次
事件静默失败,根本不知道删没删成
MySQL EVENT 默认失败不报错:哪怕存储过程中字段名写错、权限不足、表不存在,事件仍标记为“已执行”,SHOW EVENTS 看不到异常,但数据越积越多。
- 必须在存储过程中加错误捕获:
DECLARE EXIT HANDLER FOR SQLEXCEPTION+ 插入日志表 - 清理逻辑前加预查:
SELECT COUNT(*) INTO @cnt FROM ... WHERE ...,为 0 就跳过,避免空跑 - 删完建议
OPTIMIZE TABLE(仅对频繁 DELETE 的 InnoDB 表有意义,注意锁表影响) - 别信
ON COMPLETION PRESERVE—— 它只控制事件到期后是否保留定义,不保证执行成功
真正难的不是写出来,而是让清理既安全又稳定:分批、限速、校验、日志、兜底。这些细节漏掉任何一环,都可能在线上引发雪崩。











