event_scheduler未开启导致create event失败,需通过set global或my.cnf启用;云数据库通常禁用,应改用crontab调用存储过程分批清理,并确保时间字段有索引。

event_scheduler 没开,CREATE EVENT 必定失败
很多人写完 CREATE EVENT 就报错,比如 ERROR 1541: Event scheduler is not running 或看似无关的 ERROR 1547: Event execution time is in the past,其实根本原因就一个:调度器压根没启动。
必须先确认状态:SHOW VARIABLES LIKE 'event_scheduler'; —— 返回 OFF 就得开。
- 临时启用(重启失效):
SET GLOBAL event_scheduler = ON; - 永久生效:在
my.cnf的[mysqld]段加event_scheduler = ON,然后重启 MySQL - 云数据库(如阿里云 RDS、腾讯云 CDB)通常禁用该变量,此时
EVENT不可用,只能改用外部crontab
CREATE EVENT 里别直接写 DELETE,必须 CALL 存储过程
直接在 CREATE EVENT DO 后面跟 DELETE FROM ... WHERE ... 是高危操作:没法分批、没法加延时、没法做前置校验,WHERE 写错或索引失效就可能误删全表。
正确做法是让 EVENT 只干一件事:到点唤起存储过程。清理逻辑全由存储过程封装。
-
EVENT体中只写:CALL clean_old_logs(); - 存储过程中必须用
WHILE+DELETE ... LIMIT 10000分批删,每次删完加DO SLEEP(0.1) - 删前务必确认时间字段(如
created_at)有索引,否则LIMIT也挡不住全表扫描 - 别用
TRUNCATE TABLE:它不走事务、不能带条件、无法回滚
ON SCHEDULE 时间表达式要写对,NOW() 在 EVENT 里不可靠
创建周期性事件时,ON SCHEDULE EVERY 1 DAY STARTS NOW() 看似合理,但 NOW() 在事件定义时刻就被求值了,后续每次触发都用同一个时间戳,实际不会“每天更新”。
应该用 CURRENT_TIMESTAMP,它在每次事件执行时实时计算。
- 安全写法:
EVERY 1 DAY STARTS CURRENT_TIMESTAMP - 想延迟首次执行(比如建完等 1 小时再开始):
EVERY 1 HOUR STARTS CURRENT_TIMESTAMP + INTERVAL 1 HOUR -
AT只适合一次性任务,清理类长期任务别用 - 单位必须是单数:
EVERY 1 DAY✅,EVERY 1 DAYS❌
云环境或权限受限时,用 crontab 调用 mysql -e 更可靠
当 event_scheduler 被禁用、账号没有 EVENT 权限、或主从复制下事件不自动同步时,外部调度反而更可控。
Linux 上一行 crontab 就能搞定:
0 2 * * * mysql -u admin -p'xxx' -e "CALL mydb.clean_old_logs();"
- 命令可加日志重定向,出错能查:
>/var/log/clean_logs.log 2>&1 - 脚本里可先
SELECT COUNT(*)预估待删量,超阈值就发告警,避免误操作 - 比
EVENT更容易监控、重试、审计——毕竟所有调用都落在系统日志和数据库 general_log 里
真正容易被忽略的不是语法,而是时间字段有没有索引、每次删多少行、删的时候业务是否正在写入。这些细节不卡在测试环境,专挑凌晨三点高峰前爆发。











