EVENT才是MySQL中实现定时执行的机制,存储过程仅在被调用时运行;需启用event_scheduler并创建EVENT才能定时触发,云数据库常禁用此功能,须改用外部crontab调用。

存储过程本身不“定时”,EVENT 才是触发器
很多人误以为写个存储过程就能自动跑,其实存储过程只是被调用时才执行;真正实现“定期”的,是 MySQL 的 EVENT 调度器。它像一个闹钟,到点就唤起你写的存储过程。如果只建了存储过程但没配 EVENT,那它永远也不会动。
常见错误现象:执行 CREATE EVENT 报 ERROR 1541: Event scheduler is not running 或 ERROR 1547: Event execution time is in the past——这基本等于调度器根本没开,别急着改 SQL,先查状态。
- 查是否启用:
SHOW VARIABLES LIKE 'event_scheduler';,返回OFF就得开 - 临时启用(重启失效):
SET GLOBAL event_scheduler = ON; - 永久启用:在
my.cnf的[mysqld]段加event_scheduler = ON,然后重启 MySQL - 云数据库(如阿里云 RDS)通常禁用该变量,此时只能放弃
EVENT,改用外部crontab调CALL
存储过程里 DELETE 必须分批 + 限流 + 索引校验
直接写 DELETE FROM logs WHERE created_at 是高危操作。千万级表上这么干,轻则锁表几十秒,重则触发 <code>Lock wait timeout exceeded,业务写入全卡住。
安全做法是在存储过程中用循环控制每次删多少、隔多久再删:
- 必须加
LIMIT 10000(别超 5 万),否则 InnoDB 可能升级锁粒度 - 每次删完加
DO SLEEP(0.1),让出 IO 和 CPU 资源,降低主从延迟风险 - 删前务必确认
created_at字段有索引,否则LIMIT也救不了全表扫描 - 别用
TRUNCATE TABLE:它不能带WHERE,也不走事务,删错了没法回滚
示例关键片段:
DELIMITER //
CREATE PROCEDURE clean_old_logs()
BEGIN
DECLARE row_count INT DEFAULT 1;
WHILE row_count > 0 DO
DELETE FROM operation_log
WHERE create_time < DATE_SUB(NOW(), INTERVAL 30 DAY)
ORDER BY id
LIMIT 10000;
SELECT ROW_COUNT() INTO row_count;
DO SLEEP(0.1);
END WHILE;
END //
DELIMITER ;EVENT 创建时时间表达式和权限最容易踩坑
CREATE EVENT 语法看着简单,但两处细节错一个就静默失败或错时执行:
-
STARTS后必须用CURRENT_TIMESTAMP,不是NOW()—— 后者在事件定义时刻就被固化,下次触发不会更新,结果可能永远不执行 -
EVERY单位必须是单数:EVERY 1 DAY✅,EVERY 1 DAYS❌(报错ERROR 1064) - 执行事件的账号必须有
EVENT权限:GRANT EVENT ON *.* TO 'user'@'host';,普通应用账号通常没有 - 事件体中禁止出现子查询、临时表、非确定性函数(如
RAND()),否则创建失败
推荐写法(每天凌晨 0 点执行):
CREATE EVENT IF NOT EXISTS daily_clean_logs ON SCHEDULE EVERY 1 DAY STARTS CURRENT_TIMESTAMP + INTERVAL (86400 - HOUR(CURRENT_TIME)*3600 - MINUTE(CURRENT_TIME)*60 - SECOND(CURRENT_TIME)) SECOND ON COMPLETION PRESERVE ENABLE DO CALL clean_old_logs();
比删除更关键的是:你删的是真“过期数据”吗?
很多团队花大力气搭好 EVENT + 存储过程,结果发现删掉的数据还在被业务查询、下游还在依赖、甚至有些“过期”记录其实是归档前置状态。真正的清理难点从来不在技术实现,而在数据语义判断。
上线前必须确认三件事:
- “过期”的定义是否所有相关方对齐?比如“30 天未登录用户”和“30 天无更新日志”含义完全不同
- 有没有外键约束或触发器会因删除而报错?尤其涉及
ON DELETE CASCADE的场景 - 是否已做逻辑备份或快照?哪怕只是
SELECT ... INTO OUTFILE导出原始数据,也比删完才发现漏判强
最后提醒一句:OPTIMIZE TABLE 不是必选项。InnoDB 表删完后空间不还给磁盘,但会复用;除非磁盘告急或写入模式突变,否则跳过它更安全——毕竟它会锁整张表。


















