MySQL事件调度器负责定时触发,存储过程封装安全可控的清理逻辑;直接在EVENT中执行DELETE风险高,必须用存储过程实现分批删除、前置校验和资源控制。

MySQL 存储过程本身不能“定时”——它只是被调用时才执行;真正实现定时的是 EVENT 调度器,而存储过程是它最安全、最可控的执行载体。
为什么必须用存储过程包装 DELETE,而不是直接在 EVENT 里写 SQL?
直接在 CREATE EVENT ... DO DELETE FROM ... 中写大范围删除,风险极高:一旦 WHERE 条件出错(比如漏写时间字段、索引失效),可能全表误删;且无法分批控制、无法加延时让出资源、无法做前置校验。用存储过程封装后,你可以在里面加 SELECT COUNT(*) 预查、用 DELETE ... LIMIT 分片、插 DO SLEEP(0.1) 避免锁表,逻辑完全自主。
- EVENT 只负责“到点唤起”,不负责“怎么删”
- 存储过程负责“删多少、怎么删、删前确认、删后优化”
- 两者组合才是生产环境可用的清理方案
CREATE EVENT 报 ERROR 1547 或静默失败?先查 event_scheduler
这是最常卡住人的第一步。即使语法完全正确,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]段加event_scheduler=ON,然后重启 MySQL - 云数据库(如阿里云 RDS、腾讯云 CDB)通常禁用该变量,此时只能放弃 EVENT,改用外部定时任务调
CALL clean_old_logs();
存储过程里 DELETE 不加 LIMIT,线上服务可能直接卡死
日志表动辄百万行,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:它不能带条件,也不走事务,无法按时间范围清理 - 删完建议
OPTIMIZE TABLE(仅对 MyISAM 或频繁 DELETE 的 InnoDB 表有意义,需评估锁表影响)
EVENT 的 STARTS 和时间函数选 CURRENT_TIMESTAMP,别信 NOW()
NOW() 返回的是语句开始执行时的时间戳,而 CURRENT_TIMESTAMP 在 EVENT 内部会被实时求值,更可靠。尤其当事件定义里用了 STARTS NOW() + INTERVAL 1 HOUR,如果创建时刻和实际首次执行时刻有延迟(比如调度器刚开启),NOW() 会导致首次执行时间漂移。
- 推荐写法:
STARTS CURRENT_TIMESTAMP + INTERVAL 1 HOUR - 周期性事件统一用
EVERY 1 DAY,避免用秒级间隔(如EVERY 86400 SECOND),可读性差且易算错 - 时区以 MySQL 服务器系统时区为准,别依赖客户端时区;可通过
SELECT @@time_zone;确认
真正难的不是写出来,而是让清理既有效又不伤业务——分批、限速、索引、调度器状态、云厂商限制,每一步都可能成为线上事故的导火索。动手前,先在测试库跑通带 SELECT 预检的存储过程,再开 EVENT。


















