mysql菜鸟教程

专栏导航

16.5 创建定时任务

      在上一节中,我们了解了 MySQL 事件调度器的基本概念和启用方法。本节将通过多个实际案例,手把手教你如何创建不同类型的定时任务,包括一次性任务和周期性重复任务,并介绍任务的管理与监控技巧。

一、回顾:事件调度器的核心语法

创建事件的完整语法如下(摘自上一节):

CREATE EVENT [IF NOT EXISTS] 事件名
ON SCHEDULE 调度规则
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE]
[COMMENT '描述']
DO
    SQL语句;
  • 调度规则 可以是 AT(一次性)或 EVERY(重复)。

  • ON COMPLETION PRESERVE 表示事件执行后保留(对重复事件通常需要),默认是 NOT PRESERVE,即执行一次后自动删除。

  • ENABLE/DISABLE 控制事件是否激活。

二、实战:创建一次性任务

示例1:延时删除临时数据

假设我们有一个临时表 temp_cart,需要在创建后 1 小时自动清空。

CREATE EVENT clear_temp_cart
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR
DO
    TRUNCATE TABLE temp_cart;

说明

  • AT CURRENT_TIMESTAMP + INTERVAL 1 HOUR 指定在当前时间的一小时后执行。

  • TRUNCATE 比 DELETE 更快,且释放空间。

示例2:在指定时间点执行一次任务

例如,在明年元旦凌晨 00:00 将所有用户积分清零。

CREATE EVENT reset_points
ON SCHEDULE AT '2026-01-01 00:00:00'
ON COMPLETION NOT PRESERVE   -- 默认就是 NOT PRESERVE,可以不写
DO
    UPDATE users SET points = 0;

三、实战:创建周期性重复任务

示例3:每天凌晨清理过期日志

假设日志表 logs 保留最近 30 天数据,每天凌晨 3:00 自动清理。

CREATE EVENT clean_expired_logs
ON SCHEDULE EVERY 1 DAY
STARTS '2025-01-01 03:00:00'
ON COMPLETION PRESERVE   -- 重复任务必须保留
DO
    DELETE FROM logs WHERE log_date < CURDATE() - INTERVAL 30 DAY;

解释

  • EVERY 1 DAY 表示每天执行一次。

  • STARTS 指定第一次执行的时间,如果省略则立即开始。

  • ON COMPLETION PRESERVE 保证任务执行后不会被删除。

示例4:每隔 10 分钟更新统计表

我们需要将订单总额实时统计到汇总表,但为了性能,可以每 10 分钟更新一次。

CREATE EVENT update_order_stats
ON SCHEDULE EVERY 10 MINUTE
STARTS CURRENT_TIMESTAMP
ON COMPLETION PRESERVE
DO
    UPDATE daily_summary ds
    SET total_orders = (SELECT COUNT(*) FROM orders WHERE order_date = ds.date),
        total_amount = (SELECT SUM(amount) FROM orders WHERE order_date = ds.date)
    WHERE ds.date = CURDATE();

示例5:每周一生成上周报表

假设每周一凌晨 2:00 自动生成上周的销售报表,并插入到 weekly_report 表。

CREATE EVENT generate_weekly_report
ON SCHEDULE EVERY 1 WEEK
STARTS '2025-01-06 02:00:00'   -- 假设周一
ON COMPLETION PRESERVE
DO
BEGIN
    INSERT INTO weekly_report (week_start, week_end, total_sales)
    VALUES (
        DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) + 7 DAY),
        DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) + 1 DAY),
        (SELECT SUM(amount) FROM orders WHERE order_date BETWEEN ...)
    );
END;

注意:DO 子句如果是多条语句,必须用 BEGIN ... END 包裹,并且通常需要在客户端中修改分隔符(DELIMITER)来创建。例如在 MySQL 命令行中:

DELIMITER //

CREATE EVENT generate_weekly_report
ON SCHEDULE EVERY 1 WEEK
STARTS '2025-01-06 02:00:00'
ON COMPLETION PRESERVE
DO
BEGIN
    INSERT INTO weekly_report (week_start, week_end, total_sales)
    VALUES ( ... );
END //

DELIMITER ;

四、管理定时任务

查看事件状态

SHOW EVENTS\G;

输出包括事件名、状态(ENABLED/DISABLED)、类型(RECURRING/ONE TIME)、最后执行时间等。

查看事件的定义

SHOW CREATE EVENT clean_expired_logs\G;

暂停/启用事件

ALTER EVENT clean_expired_logs DISABLE;
ALTER EVENT clean_expired_logs ENABLE;

修改事件的调度规则

例如将清理日志改为每天凌晨 4:00 执行:

ALTER EVENT clean_expired_logs
ON SCHEDULE EVERY 1 DAY
STARTS '2025-01-01 04:00:00';

删除事件

DROP EVENT IF EXISTS clean_expired_logs;

事件执行状态监控

要查看事件的历史执行情况,可以查询 mysql.event 表或使用 performance_schema 中的相关表(如 events_statements_current),但最简单的方法是:

  • 在事件体内插入日志记录到自定义的日志表。

  • 或者在事件执行后观察数据变化。

示例:在事件中添加日志输出(如果事件简单,可以不记录,但复杂事件建议记录)。

CREATE TABLE event_log (
    log_id INT AUTO_INCREMENT PRIMARY KEY,
    event_name VARCHAR(64),
    executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 在事件中插入日志
CREATE EVENT my_event
ON SCHEDULE EVERY 1 HOUR
DO
BEGIN
    -- 业务逻辑
    UPDATE ... ;
    -- 记录执行
    INSERT INTO event_log (event_name) VALUES ('my_event');
END;

五、最佳实践与注意事项

  1. 时区一致性:确保 MySQL 服务器的时区设置正确,否则 CURDATE() 等函数可能不符合预期。可以在事件内使用 CONVERT_TZ 转换时区。

  2. 错误处理:事件内语句出错可能导致事件失败,并且不会自动重试。可以在事件体内使用 DECLARE CONTINUE HANDLER 捕获异常,并记录错误。

  3. 资源控制:避免事件执行时间过长,尤其是高峰时段。对于大批量数据操作,可以分批次处理(如 LIMIT 分批删除)。

  4. 事件调度器可靠性:如果 MySQL 重启,调度器状态由配置决定。确保 event_scheduler 设为 ON 并写入配置文件。

  5. 权限:创建事件需要 EVENT 权限,执行事件需要操作相关表的权限。

  6. 事件命名:采用清晰的前缀,例如 event_clean_event_stats_,方便管理。

小结

  • 通过 CREATE EVENT 可以轻松创建定时任务,替代外部 cron 脚本。

  • 一次性任务使用 AT,周期性任务使用 EVERY

  • 使用 ALTER EVENT 修改任务属性,DROP EVENT 删除任务。

  • 结合日志记录,可以监控任务的执行情况。

  • 合理利用事件调度器,可以让数据库自动化维护工作变得简单高效。


所有评论

关于我 备案号:蜀ICP备2023042032号-1