mysql event调度器默认关闭,create event会因调度器未启用而报错或静默失败;需通过show variables检查,用set global临时开启或在配置文件中永久启用;云数据库可能禁用该功能,需改用外部定时任务调用存储过程。

MySQL Event 调度器没启用,CREATE EVENT 会直接报错
默认 MySQL 关闭了事件调度器,即使语法全对,执行 CREATE EVENT 也会静默失败或提示 ERROR 1547 (HY000): Event execution time is in the past(其实是调度器没开的伪装错误)。必须先确认并启用:
- 查状态:
SHOW VARIABLES LIKE 'event_scheduler';—— 返回OFF就得开 - 临时启用(重启失效):
SET GLOBAL event_scheduler = ON; - 永久启用:在
my.cnf或mysqld.cnf的[mysqld]段落加一行event_scheduler=ON
注意:有些云数据库(如阿里云 RDS、腾讯云 CDB)默认禁用且不开放 event_scheduler 配置权限,这时就得换方案——比如用外部定时任务调 mysql -e "CALL clean_old_logs();"。
存储过程里用 DELETE 清日志,不加 LIMIT 容易锁表卡住业务
日志表动辄几百万行,DELETE FROM log_table WHERE created_at 这种语句若没限制,可能一次性扫描+锁住大量行,拖慢甚至阻塞线上查询。尤其在高并发写入场景下,容易触发 <code>Lock wait timeout exceeded。
- 务必分批删:在存储过程中用
DELETE ... LIMIT 10000循环,每次删完加DO SLEEP(0.1)让出资源 - 确保
created_at字段有索引,否则WHERE条件走全表扫描,LIMIT也救不了 - 别用
TRUNCATE—— 它不能带条件,也无法配合时间范围清理
示例片段:
REPEAT DELETE FROM sys_log WHERE created_at <h3>Event 时间表达式写成 <code>AT</code> 而非 <code>EVERY</code>,只会执行一次就停</h3> <p>想每天凌晨 2 点自动跑清理,如果写成 <code>AT '2024-01-01 02:00:00'</code>,那它只在那个时间点执行一次,之后再不会触发。必须用 <code>EVERY</code> + <code>STARTS</code> 组合。</p>
- 正确写法:
EVERY 1 DAY STARTS '2024-01-01 02:00:00' - 时区要注意:MySQL Event 默认按系统时区执行,如果服务器是 UTC,但你想按东八区时间执行,得在
STARTS里显式写'2024-01-01 02:00:00' + INTERVAL 8 HOUR,或统一把 MySQL 时区设为+08:00 - 修改已存在 Event 要用
ALTER EVENT,不能直接改定义;禁用/启用用ALTER EVENT e_clean_log DISABLE/ENABLE
清理逻辑放在存储过程里,不是为了“封装”,而是为了可测试、可复用、可手动触发
有人图省事把 DELETE 语句直接塞进 Event 定义里,结果出问题没法调试,也没法手动验证逻辑是否安全。放到存储过程里,好处很实在:
- 能单独执行
CALL clean_old_logs();测试效果,看影响行数、耗时、有没有锁冲突 - 多个 Event(比如日志、缓存、临时表)可以复用同一套清理逻辑,只需改参数
- 存储过程里可以用
SELECT ROW_COUNT()记录清理数量到监控表,方便后续排查 - 万一 Event 某次失败,手动补跑比重置调度器简单得多
真正麻烦的从来不是写 Event,而是日志表字段类型不统一(比如有的用 DATETIME,有的用 TIMESTAMP)、分区表没配好导致 DELETE 无法命中分区、或者误删了还没归档的审计数据——这些细节,比语法本身更值得花时间核对。











