乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 数据库基础与安装

    • MySQL 简介
    • MySQL 5.6 到 5.7 到 8.0 关键差异速查
    • 安装 MySQL 5.7
    • 连接与断开服务器
    • 创建数据库
    • 创建数据表
    • 数据库与数据表
    • 加载数据
    • 获取数据库信息
    • 批处理模式
    • SHOW 语句汇总
    • FLUSH 与 RESET 语句
    • my.cnf 核心参数
    • 字符集与排序规则
  • 第2章 SQL基础查询

    • SELECT
    • WHERE
    • ORDER BY
    • LIMIT
    • COUNT
    • 聚合函数
    • 比较运算符
    • 逻辑运算符
    • 算术运算符
    • 模式匹配
    • NULL 值处理
    • UPDATE
    • DELETE
    • REPLACE
    • SELECT INTO
  • 第3章 数据类型与运算符

    • 数值类型
    • 字符串类型
    • 日期时间类型
    • BIT 类型
    • ENUM 类型
    • SET 类型
    • JSON 类型
    • 类型转换
  • 第4章 函数与表达式

    • 字符串函数
    • 数值函数
    • 日期函数
    • 全文检索函数
  • 第5章 高级查询与子查询

    • JOIN
    • 子查询
    • UNION
    • GROUP BY
    • HAVING
    • DISTINCT
  • 第6章 表与索引

    • 数据定义语言
    • 修改表结构
    • 视图
    • 修改视图与检查选项
    • 外键
    • 索引
    • 唯一索引
    • 复合索引
    • 存储引擎对比
    • 分区表
    • 第一范式与第二范式
    • 第三范式与 BC 范式
    • 反范式设计
  • 第7章 存储过程与函数

    • 存储过程
    • 存储函数
    • 变量
    • 流程控制
    • 游标
    • 预处理语句
  • 第8章 事务与锁

    • 事务
    • 事务隔离级别
    • 锁机制
    • MVCC
    • 死锁专题分析
    • LOCK TABLES
    • XA 事务
  • 第9章 用户管理与安全

    • 用户管理
    • 权限管理
    • 角色
    • SQL 注入防范
  • 第10章 性能优化入门

    • 执行计划
    • 索引优化
    • 查询优化
    • 查询优化器提示
    • 慢查询日志
    • InnoDB 深入机制
    • InnoDB 专项优化
    • Performance Schema
    • sys Schema
  • 第11章 复制与高可用

    • 主从复制原理
    • 半同步复制配置
    • binlog 开启与 point-in-time 恢复
    • mysqldump 全库备份
    • mysqldump 单表与条件备份
    • mysqldump 恢复与导入
    • xtrabackup 全量热备
    • xtrabackup 准备与恢复
    • xtrabackup 增量与流式备份
  • 第12章 触发器与事件

    • 触发器
    • 事件调度器
  • 参考

    • MySQL 5.7 专业术语大全
    • MySQL 5.7 关键字与保留字大全

事件调度器

导学

MySQL 事件调度器(Event Scheduler)是数据库内置的"定时任务"系统。无需借助外部 Cron,你就可以让 MySQL 在指定时间或按周期自动执行 SQL。

定义

事件调度器(Event Scheduler):MySQL 的后台线程,用于在预定的时间点或按预定周期自动执行指定的事件(SQL 语句集合)。

启用事件调度器

-- 查看事件调度器当前状态
SHOW VARIABLES LIKE 'event_scheduler';

结果示例:

Variable_nameValue
event_schedulerOFF
-- 临时启用(重启后失效)
SET GLOBAL event_scheduler = ON;

-- 确认已开启
SHOW VARIABLES LIKE 'event_scheduler';

结果示例:

Variable_nameValue
event_schedulerON
-- 永久启用(需修改 my.cnf 配置文件)
-- 在 [mysqld] 段添加:
-- event_scheduler = ON

注意:如果 event_scheduler 为 DISABLED,表示 MySQL 编译时未启用事件调度器支持,此时无法通过 SET GLOBAL 开启。

核心语法

CREATE EVENT event_name
ON SCHEDULE
    {AT timestamp [+ INTERVAL interval] | EVERY interval [STARTS timestamp] [ENDS timestamp]}
[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE]
[COMMENT '说明文字']
DO
    sql_statement;
子句说明
AT timestamp一次性事件,在指定时间点执行
EVERY interval周期性事件,按指定间隔重复执行
STARTS timestamp周期性事件首次执行时间
ENDS timestamp周期性事件结束时间
ON COMPLETION PRESERVE事件执行完成后保留,不自动删除
ON COMPLETION NOT PRESERVE事件执行完成后自动删除(默认)
ENABLE / DISABLE创建时即启用或禁用

SQL 示例

场景一:一次性事件(定时清理过期日志)

第一步:建立测试环境

-- 创建日志表
CREATE TABLE logs (
    log_id INT AUTO_INCREMENT PRIMARY KEY,
    emp_id INT,
    action VARCHAR(20),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 插入测试数据(模拟不同时间产生的日志)
INSERT INTO logs (emp_id, action, created_at) VALUES
(1, 'LOGIN', DATE_SUB(NOW(), INTERVAL 10 DAY)),
(2, 'LOGOUT', DATE_SUB(NOW(), INTERVAL 5 DAY)),
(1, 'UPDATE', DATE_SUB(NOW(), INTERVAL 0 DAY));

当前数据状态(logs 表):

log_idemp_idactioncreated_at
11LOGIN2026-05-31 15:00:00
22LOGOUT2026-06-05 15:00:00
31UPDATE2026-06-10 15:00:00

假设当前日期为 2026-06-10,清理策略为删除 7 天前的日志。

第二步:创建一次性清理事件

DELIMITER //

CREATE EVENT IF NOT EXISTS event_delete_old_logs
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 MINUTE
ON COMPLETION NOT PRESERVE
ENABLE
DO
BEGIN
    DELETE FROM logs WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 DAY);
END //

DELIMITER ;

第三步:查看事件是否创建成功

SHOW EVENTS WHERE Name = 'event_delete_old_logs';

结果示例:

DbNameDefinerTime zoneTypeExecute atInterval valueInterval fieldStartsEndsStatus
testevent_delete_old_logsroot@localhostSYSTEMONE TIME2026-06-10 15:01:00NULLNULLNULLNULLENABLED

第四步:等待事件执行后查看结果

-- 1 分钟后查询日志表
SELECT * FROM logs;

操作后的数据状态(logs 表):

log_idemp_idactioncreated_at
22LOGOUT2026-06-05 15:00:00
31UPDATE2026-06-10 15:00:00

结果解读:

  • log_id = 1 的日志因为超过 7 天(created_at < DATE_SUB(NOW(), INTERVAL 7 DAY)),已被事件自动删除。
  • log_id = 2(5 天前)和 log_id = 3(当天)符合保留条件,仍然存在。
  • 由于使用了 ON COMPLETION NOT PRESERVE,事件执行一次后自动从系统中删除。可以再次执行 SHOW EVENTS 确认该事件已不存在。

场景二:周期性事件(每日日志清理与表优化)

第一步:建立测试环境

-- 继续使用 logs 表,补充更多历史数据
INSERT INTO logs (emp_id, action, created_at) VALUES
(1, 'LOGIN', DATE_SUB(NOW(), INTERVAL 35 DAY)),
(2, 'LOGOUT', DATE_SUB(NOW(), INTERVAL 32 DAY)),
(1, 'DELETE', DATE_SUB(NOW(), INTERVAL 31 DAY)),
(2, 'UPDATE', DATE_SUB(NOW(), INTERVAL 15 DAY)),
(1, 'LOGIN', DATE_SUB(NOW(), INTERVAL 2 DAY));

当前数据状态(logs 表):

log_idemp_idactioncreated_at
11LOGIN2026-05-31 15:00:00
22LOGOUT2026-06-05 15:00:00
31UPDATE2026-06-10 15:00:00
41LOGIN2026-05-06 15:00:00
52LOGOUT2026-05-09 15:00:00
61DELETE2026-05-10 15:00:00
72UPDATE2026-05-26 15:00:00
81LOGIN2026-06-08 15:00:00

清理策略:删除 30 天前的操作日志,并执行表优化。

第二步:创建周期性清理事件

DELIMITER //

CREATE EVENT IF NOT EXISTS event_daily_cleanup
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_TIMESTAMP + INTERVAL 1 MINUTE
ON COMPLETION PRESERVE
ENABLE
COMMENT '每日清理30天前的日志并优化表'
DO
BEGIN
    DELETE FROM logs WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY);
    OPTIMIZE TABLE logs;
END //

DELIMITER ;

第三步:查看事件状态

SHOW EVENTS WHERE Name = 'event_daily_cleanup';

结果示例:

DbNameDefinerTime zoneTypeExecute atInterval valueInterval fieldStartsEndsStatus
testevent_daily_cleanuproot@localhostSYSTEMRECURRINGNULL1DAY2026-06-10 15:01:00NULLENABLED

第四步:手动触发查看效果(或等待首次执行)

-- 等待 1 分钟后查询
SELECT * FROM logs;

操作后的数据状态(logs 表):

log_idemp_idactioncreated_at
22LOGOUT2026-06-05 15:00:00
31UPDATE2026-06-10 15:00:00
72UPDATE2026-05-26 15:00:00
81LOGIN2026-06-08 15:00:00

结果解读:

  • log_id = 1, 4, 5, 6 的日志超过 30 天,已被周期性事件自动清理。
  • log_id = 2, 3, 7, 8 的日志在 30 天内,被保留。
  • 由于使用了 ON COMPLETION PRESERVE,该事件会持续每天执行,直到被手动删除或到达 ENDS 时间。
  • OPTIMIZE TABLE 会重组表数据,回收碎片空间,提升查询效率。

场景三:定时数据统计(每日报表生成)

第一步:建立测试环境

-- 创建员工表(含入职时间)
CREATE TABLE employees (
    emp_id INT AUTO_INCREMENT PRIMARY KEY,
    emp_name VARCHAR(20),
    dept VARCHAR(20),
    score DECIMAL(5,2),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 创建每日统计表
CREATE TABLE daily_stats (
    stat_id INT AUTO_INCREMENT PRIMARY KEY,
    stat_date DATE,
    new_employees INT,
    total_employees INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 插入测试数据(模拟不同日期入职的员工)
INSERT INTO employees (emp_name, dept, score, created_at) VALUES
('大翔', '技术部', 100, DATE_SUB(NOW(), INTERVAL 2 DAY)),
('白歌', '技术部', NULL, DATE_SUB(NOW(), INTERVAL 0 DAY));

当前数据状态(employees 表):

emp_idemp_namedeptscorecreated_at
1大翔技术部1002026-06-08 10:00:00
2白歌技术部NULL2026-06-10 11:00:00

当前数据状态(daily_stats 表,尚未生成):

SELECT * FROM daily_stats;

结果:空表

第二步:创建定时统计事件

DELIMITER //

CREATE EVENT IF NOT EXISTS event_daily_report
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_DATE + INTERVAL 1 DAY
ON COMPLETION PRESERVE
ENABLE
COMMENT '每日凌晨统计当日新增员工数和总员工数'
DO
BEGIN
    INSERT INTO daily_stats (stat_date, new_employees, total_employees)
    SELECT
        CURDATE(),
        (SELECT COUNT(*) FROM employees WHERE DATE(created_at) = CURDATE()),
        (SELECT COUNT(*) FROM employees);
END //

DELIMITER ;

第三步:手动模拟事件执行效果

-- 手动执行事件中的逻辑,查看效果
INSERT INTO daily_stats (stat_date, new_employees, total_employees)
SELECT
    CURDATE(),
    (SELECT COUNT(*) FROM employees WHERE DATE(created_at) = CURDATE()),
    (SELECT COUNT(*) FROM employees);

操作后的数据状态(daily_stats 表):

stat_idstat_datenew_employeestotal_employeescreated_at
12026-06-10122026-06-10 15:00:00

结果解读:

  • stat_date 为统计日期(当天)。
  • new_employees = 1 表示当天(2026-06-10)新增了 1 名员工(白歌)。
  • total_employees = 2 表示员工表当前总记录数为 2。
  • 该事件配置为从次日开始每天凌晨执行,持续生成每日统计报表。

场景四:带结束时间的周期性事件

第一步:建立测试环境

基于前面已建立的 employees 表。

当前数据状态(employees 表):

emp_idemp_namedeptscorecreated_at
1大翔技术部1002026-06-08 10:00:00
2白歌技术部NULL2026-06-10 11:00:00

员工白歌的 score 为 NULL,需要自动修正为 0。

第二步:创建带结束时间的自动修正事件

DELIMITER //

CREATE EVENT IF NOT EXISTS event_fix_null_score
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_TIMESTAMP + INTERVAL 1 MINUTE
ENDS CURRENT_TIMESTAMP + INTERVAL 30 DAY
ON COMPLETION PRESERVE
ENABLE
COMMENT '每日检查并修正 score 为 NULL 的记录,30天后自动停止'
DO
BEGIN
    UPDATE employees
    SET score = 0
    WHERE score IS NULL;
END //

DELIMITER ;

第三步:查看事件详情

SHOW EVENTS WHERE Name = 'event_fix_null_score';

结果示例:

DbNameDefinerTypeStartsEndsStatus
testevent_fix_null_scoreroot@localhostRECURRING2026-06-10 15:01:002026-07-10 15:00:00ENABLED

第四步:等待事件执行后查看结果

SELECT * FROM employees;

操作后的数据状态(employees 表):

emp_idemp_namedeptscorecreated_at
1大翔技术部1002026-06-08 10:00:00
2白歌技术部02026-06-10 11:00:00

结果解读:

  • emp_id = 2(白歌)的 score 因为 IS NULL,已被事件自动修正为 0。
  • 该事件配置了 ENDS CURRENT_TIMESTAMP + INTERVAL 30 DAY,30 天后会自动停止执行并保留在系统中。
  • 适用于临时数据修复等需要自动收尾的业务场景。

场景五:禁用、启用和删除事件

基于前面已创建的 event_daily_cleanup 事件,演示管理操作。

查看当前所有事件:

SHOW EVENTS;

结果示例:

DbNameTypeStatus
testevent_daily_cleanupRECURRINGENABLED
testevent_fix_null_scoreRECURRINGENABLED

禁用事件:

ALTER EVENT event_daily_cleanup DISABLE;

-- 确认已禁用
SHOW EVENTS WHERE Name = 'event_daily_cleanup';

结果示例:

DbNameTypeStatus
testevent_daily_cleanupRECURRINGDISABLED

启用事件:

ALTER EVENT event_daily_cleanup ENABLE;

-- 确认已启用
SHOW EVENTS WHERE Name = 'event_daily_cleanup';

结果示例:

DbNameTypeStatus
testevent_daily_cleanupRECURRINGENABLED

修改事件执行计划:

ALTER EVENT event_daily_cleanup
ON SCHEDULE EVERY 12 HOUR
STARTS CURRENT_TIMESTAMP + INTERVAL 10 MINUTE;

查看事件创建语句:

SHOW CREATE EVENT event_daily_cleanup;

删除事件:

DROP EVENT IF EXISTS event_daily_cleanup;

-- 确认已删除
SHOW EVENTS WHERE Name = 'event_daily_cleanup';

结果:无记录返回,事件已删除。

结果解读:

  • ALTER EVENT ... DISABLE/ENABLE 可以临时停用或恢复事件,常用于维护窗口期间暂停定时任务。
  • ALTER EVENT 还可以修改事件的调度计划、执行内容等。
  • DROP EVENT IF EXISTS 安全删除事件,IF EXISTS 避免事件不存在时报错。
  • 删除后该事件将不再执行,已产生的数据不受影响。

场景六:事件执行日志记录(最佳实践)

第一步:建立测试环境

-- 创建事件执行日志表
CREATE TABLE event_execution_logs (
    log_id INT AUTO_INCREMENT PRIMARY KEY,
    event_name VARCHAR(100),
    start_time DATETIME,
    end_time DATETIME,
    rows_affected INT,
    status VARCHAR(20),
    error_message TEXT
);

-- 继续使用 logs 表,补充测试数据
INSERT INTO logs (emp_id, action, created_at) VALUES
(1, 'OLD_LOGIN', DATE_SUB(NOW(), INTERVAL 10 DAY)),
(2, 'OLD_LOGOUT', DATE_SUB(NOW(), INTERVAL 8 DAY)),
(1, 'NEW_LOGIN', DATE_SUB(NOW(), INTERVAL 1 DAY));

当前数据状态(logs 表):

log_idemp_idactioncreated_at
11LOGIN2026-05-31 15:00:00
22LOGOUT2026-06-05 15:00:00
31UPDATE2026-06-10 15:00:00
72UPDATE2026-05-26 15:00:00
81LOGIN2026-06-08 15:00:00
91OLD_LOGIN2026-05-31 15:00:00
102OLD_LOGOUT2026-06-02 15:00:00
111NEW_LOGIN2026-06-09 15:00:00

第二步:创建带日志记录的事件

DELIMITER //

CREATE EVENT IF NOT EXISTS event_cleanup_with_log
ON SCHEDULE EVERY 1 DAY
STARTS CURRENT_TIMESTAMP + INTERVAL 1 MINUTE
ON COMPLETION PRESERVE
ENABLE
DO
BEGIN
    DECLARE v_start DATETIME DEFAULT NOW();
    DECLARE v_rows INT DEFAULT 0;
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        INSERT INTO event_execution_logs (event_name, start_time, end_time, status, error_message)
        VALUES ('event_cleanup_with_log', v_start, NOW(), 'FAILED', SQLERRM());
    END;

    DELETE FROM logs WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 DAY);
    SET v_rows = ROW_COUNT();

    INSERT INTO event_execution_logs (event_name, start_time, end_time, rows_affected, status)
    VALUES ('event_cleanup_with_log', v_start, NOW(), v_rows, 'SUCCESS');
END //

DELIMITER ;

第三步:等待事件执行后查看结果

SELECT * FROM logs;

操作后的数据状态(logs 表):

log_idemp_idactioncreated_at
22LOGOUT2026-06-05 15:00:00
31UPDATE2026-06-10 15:00:00
72UPDATE2026-05-26 15:00:00
81LOGIN2026-06-08 15:00:00
111NEW_LOGIN2026-06-09 15:00:00
SELECT * FROM event_execution_logs;

操作后的数据状态(event_execution_logs 表):

log_idevent_namestart_timeend_timerows_affectedstatuserror_message
1event_cleanup_with_log2026-06-10 15:01:002026-06-10 15:01:003SUCCESSNULL

结果解读:

  • logs 表中超过 7 天的 3 条记录(log_id = 1, 9, 10)已被清理。
  • event_execution_logs 表自动记录了事件的执行时间、影响行数和状态。
  • 使用 DECLARE EXIT HANDLER FOR SQLEXCEPTION 捕获异常,即使执行失败也会记录错误信息,便于后续排查。
  • 这是事件调度器的最佳实践:务必自行记录执行日志,因为 MySQL 不会为事件保留内置执行历史。

事件调度器的限制

限制说明
依赖 MySQL 进程MySQL 停止时事件不会执行,重启后也不会补执行
调度粒度最小间隔是 1 秒,不支持 Cron 表达式(如"每周一 3 点"需要额外处理)
无内置历史记录执行成功或失败不会在 MySQL 中留下日志,需自行记录
主从复制默认事件在主库执行,从库不执行(super_read_only 时)

常见误区

误区正解
事件调度器可以替代 Linux Cron简单任务可以,但复杂调度(如"每月最后一个周五")、外部脚本调用仍需 Cron。
事件执行失败会通知我不会。MySQL 不会主动通知事件执行失败,需自行在事件逻辑中记录日志或发送告警。
事件调度器默认开启默认是关闭的,需要手动开启 event_scheduler。

面试考点

Q:MySQL 事件调度器和 Linux Cron 的区别?

事件调度器是 MySQL 内置的,适合纯数据库操作(清理、统计),无需外部依赖;Cron 是操作系统级的,适合调用外部脚本、更复杂的调度逻辑。事件调度器在 MySQL 停止时不执行。

Q:如何确保事件在主从架构下只执行一次?

MySQL 5.7 中,事件默认在主库执行,从库不执行(因为 super_read_only 阻止了事件的写入)。但需谨慎验证,避免双主架构下的重复执行。

Q:事件执行失败怎么排查?

MySQL 错误日志可能记录事件执行错误,但信息有限。最佳实践是在事件逻辑中加入异常处理(DECLARE CONTINUE HANDLER)并将执行结果写入日志表。

小结

  • 事件调度器让 MySQL 具备定时执行任务的能力
  • 支持一次性事件(AT)和周期性事件(EVERY)
  • 默认关闭,需启用 event_scheduler
  • 适合数据库内部维护任务,复杂调度仍需外部工具
  • 最佳实践:在事件内部记录执行日志,监控执行状态和影响行数

下一章引子:数据库自动化能力讲完了,接下来进入数据库安全领域——用户管理和权限控制。

上一页
触发器