MySQL用事件调度器做定时任务

MySQL 自带事件调度器(Event Scheduler),不依赖 cron 或外部工具,就能在数据库层面跑定时任务。这篇把事件调度器的开启方式、创建语法、常见场景和注意事项都过一遍。

很多人跑定时任务第一反应是 Linux 的 cron,或者在应用层起个定时线程。

但如果任务本身就是数据库操作——比如定期清日志、定时汇总统计数据——直接用 MySQL 自带的事件调度器往往更简单,不用多维护一套外部调度。

事件调度器是什么

MySQL 5.1 开始内置了事件调度器(Event Scheduler)。

它的作用很直白:按照你定义的时间规则,自动执行一段 SQL 语句。

可以理解成数据库层面的 cron,只不过执行的是 SQL,不是 Shell 命令。

先把调度器开起来

事件调度器默认是关闭的,先检查状态:

SHOW VARIABLES LIKE 'event_scheduler';
​

如果是 OFF,临时开启:

SET GLOBAL event_scheduler = ON;
​

这个方式重启 MySQL 后会失效。要持久生效,得在 my.cnf(Linux)或 my.ini(Windows)里加上:

[mysqld]
event_scheduler = ON
​

改完重启 MySQL 服务生效。

创建一个事件

基础语法:

CREATE EVENT event_name
ON SCHEDULE schedule
DO
  sql_statement;
​

schedule 有两种形式:

  • AT timestamp:在某个固定时间点执行一次
  • EVERY interval:按间隔重复执行

一次性执行

比如 5 分钟后删一批测试数据:

CREATE EVENT clean_test_data
ON SCHEDULE AT NOW() + INTERVAL 5 MINUTE
DO
  DELETE FROM mebugs_log WHERE type = 'test';
​

执行完自动销毁,不用手动清理。

周期性执行

每天凌晨 2 点清理 30 天前的操作日志:

CREATE EVENT daily_clean_log
ON SCHEDULE EVERY 1 DAY
STARTS '2026-01-01 02:00:00'
DO
  DELETE FROM mebugs_operate_log
  WHERE created_at < NOW() - INTERVAL 30 DAY;
​

STARTS 指定第一次执行时间,之后每隔 1 天执行一次。

不加 ENDS 就是一直跑,加了 ENDS 会在指定时间后停止。

执行多条语句

DO 后面只能跟一条 SQL。要执行多条,用 BEGIN...END 包起来:

DELIMITER $$

CREATE EVENT monthly_summary
ON SCHEDULE EVERY 1 MONTH
STARTS '2026-01-01 03:00:00'
DO
BEGIN
  -- 汇总上月数据
  INSERT INTO mebugs_monthly_stat (month, pv, uv, created_at)
  SELECT
    DATE_FORMAT(NOW() - INTERVAL 1 MONTH, '%Y-%m'),
    COUNT(*),
    COUNT(DISTINCT user_id),
    NOW()
  FROM mebugs_access_log
  WHERE created_at >= DATE_FORMAT(NOW() - INTERVAL 1 MONTH, '%Y-%m-01')
    AND created_at < DATE_FORMAT(NOW(), '%Y-%m-01');

  -- 清理三个月前的明细日志
  DELETE FROM mebugs_access_log
  WHERE created_at < NOW() - INTERVAL 3 MONTH;
END$$

DELIMITER ;
​

注意要用 DELIMITER 切换语句分隔符,不然 BEGIN...END 里的分号会被 MySQL 客户端提前截断。

管理已有事件

查看当前库所有事件:

SHOW EVENTS;
-- 或者更详细的
SELECT * FROM information_schema.EVENTS WHERE EVENT_SCHEMA = 'site_ol'\G
​

暂停和恢复:

-- 暂停事件
ALTER EVENT daily_clean_log DISABLE;

-- 恢复事件
ALTER EVENT daily_clean_log ENABLE;
​

修改执行时间:

ALTER EVENT daily_clean_log
ON SCHEDULE EVERY 1 DAY
STARTS '2026-01-01 03:30:00';
​

删除事件:

DROP EVENT IF EXISTS daily_clean_log;
​

几个坑和注意事项

事件调度器是全局开关

event_scheduler 是全局变量,一关就是整个 MySQL 实例上所有库的事件全停。

如果你在共享数据库环境里操作,关之前确认一下别人有没有在跑事件。

事件没有执行日志

这是比较蛋疼的地方——MySQL 默认不记录事件的执行历史,跑了没跑、成功还是失败,你不知道。

如果需要追踪,可以在事件的 DO 逻辑里手动 INSERT 一条执行记录到日志表:

INSERT INTO mebugs_event_log (event_name, executed_at, status)
VALUES ('daily_clean_log', NOW(), 'done');
​

时区问题

STARTS 里的时间是按 MySQL 服务器的时区解释的。

如果你的应用服务器和数据库服务器时区不一致,要留意,不然定时时间会偏。

用 SELECT NOW(), @@global.time_zone, @@session.time_zone; 确认一下时区再配时间。

主从复制环境下的注意

事件调度器默认只在主库上跑,从库上即使有相同的事件定义,event_scheduler 也是 OFF 的。

主从切换(比如故障切换或主动 Failover)后,新主库上要手动把事件调度器开起来,不然事件会停跑。

适合用事件调度器的场景

我自己会考虑用事件调度器的场景大概这几类:

  • 定期清理历史日志、过期数据
  • 定时汇总统计数据(日/月报表预聚合)
  • 定时更新某个汇总字段(比如更新文章的阅读量均值)
  • 一次性的延迟执行(比如 N 分钟后把某条记录标记为超时)

不太适合的场景:需要根据执行结果做分支处理、需要跨库跨服务的协调、对执行精度要求极高(秒级以内)。

这类情况还是老老实实上专业的调度框架(xxl-job、Quartz 这类)比较稳。

小结

MySQL 事件调度器用起来挺轻巧:

  • 开启 event_scheduler = ON
  • CREATE EVENT 定义时间规则和要执行的 SQL
  • 多条语句用 BEGIN...END 包裹
  • 用 ALTER EVENT 管理事件状态

适合纯数据库层面的定时维护任务,不需要额外维护外部调度服务。

需要执行历史的话,记得自己建一张日志表,手动记录每次执行情况。

更多推荐

章节目录