图源:ubiq.co
在 MySQL 中,用事件(Event)表示和定义一个定时任务。
我们可以利用事件执行一些定时任务,比如定期生成统计数据、清理和转储日志表等。
在使用事件之前需要先检查 MySQL 是否开启了事件功能:
mysql> SELECT @@event_scheduler;
+-------------------+
| @@event_scheduler |
+-------------------+
| ON |
+-------------------+
1 row in set (0.00 sec)
或使用:
mysql> SHOW VARIABLES LIKE 'event%';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | ON |
+-----------------+-------+
1 row in set, 1 warning (0.01 sec)
ON
表示开启,OFF
表示关闭。
可以通过以下语句启用和关闭事件功能:
SET GLOBAL event_scheduler = ON;
SET GLOBAL event_scheduler = OFF;
这样的方式开启或关闭事件会在 MySQl 重启后失效,如果有需要,可以在 MySQL 的配置文件my.cnf
中添加:
event_scheduler=1
用 SQLyog 创建事件会出现以下创建语句模版:
DELIMITER $$
-- SET GLOBAL event_scheduler = ON$$ -- required for event to execute but not create
CREATE /*[DEFINER = { user | CURRENT_USER }]*/ EVENT `jpa`.`stat_student_score`
ON SCHEDULE
/* uncomment the example below you want to use */
-- scheduleexample 1: run once
-- AT 'YYYY-MM-DD HH:MM.SS'/CURRENT_TIMESTAMP { + INTERVAL 1 [HOUR|MONTH|WEEK|DAY|MINUTE|...] }
-- scheduleexample 2: run at intervals forever after creation
-- EVERY 1 [HOUR|MONTH|WEEK|DAY|MINUTE|...]
-- scheduleexample 3: specified start time, end time and interval for execution
/*EVERY 1 [HOUR|MONTH|WEEK|DAY|MINUTE|...]
STARTS CURRENT_TIMESTAMP/'YYYY-MM-DD HH:MM.SS' { + INTERVAL 1[HOUR|MONTH|WEEK|DAY|MINUTE|...] }
ENDS CURRENT_TIMESTAMP/'YYYY-MM-DD HH:MM.SS' { + INTERVAL 1 [HOUR|MONTH|WEEK|DAY|MINUTE|...] } */
/*[ON COMPLETION [NOT] PRESERVE]
[ENABLE | DISABLE]
[COMMENT 'comment']*/
DO
BEGIN
(sql_statements)
END$$
DELIMITER ;
创建事件的语句可以分为以下部分:
CREATE ... EVENT ...
,定义事件名称。包含一个可选的DEFINER
语句,可以指定用户的使用权限。ON SCHEDULE ...
,定义事件执行的时间点或周期,可以用STARTS
和ENDS
指定事件的有效期。ON COMPLETION [NOT] PRESERVE
,可选,如果设置成``ON COMPLETION PRESERVE,事件到期后依然会被保留,如果设置成
ON COMPLETION NOT PRESERVE`,事件到期后会被删除。[ENABLE | DISABLE]
,可选,ENABLE
为启用,DISABLE
为停用。[COMMENT 'comment']
,可选,注释。DO ...
,事件执行的具体 SQL,如果是多条,可以使用BEGIN
和END
包裹。下面我们看几个具体示例。
下面这个事件将在指定时间点运行:
DELIMITER $$
CREATE EVENT `jpa`.`insert_sys_msg`
ON SCHEDULE
AT '2023-07-11 15:59:00'
DO
BEGIN
INSERT INTO `jpa`.`sys_msg` (`msg`, `time`) VALUES ('hello', NOW());
END$$
DELIMITER ;
结果:
mysql> select * from sys_msg;
+----+-------+---------------------+
| id | msg | time |
+----+-------+---------------------+
| 1 | 222 | 2023-07-11 15:54:14 |
| 2 | hello | 2023-07-11 15:59:00 |
+----+-------+---------------------+
还可以观察到这个事件已经被从服务器自动删除了,所以事件默认是ON COMPLETION NOT PRESERVE
。
除了直接指定时间字符串以外,还可以用当前时间点+时间段的方式指定在某个时间段后执行:
DELIMITER $$
CREATE EVENT `jpa`.`create_sys_msg`
ON SCHEDULE
AT CURRENT_TIMESTAMP + INTERVAL 2 MINUTE
DO
BEGIN
INSERT INTO `jpa`.`sys_msg` (`msg`, `time`) VALUES ('hello', NOW());
END$$
DELIMITER ;
这里的AT CURRENT_TIMESTAMP + INTERVAL 2 MINUTE
意味着事件将在创建好后的2分钟后执行。
就像模版 SQL 中的注释内容那样,时间段的单位可以是[HOUR|MONTH|WEEK|DAY|MINUTE|...]
中的任意一种。
大多数定时任务都会被要求以固定的事件间隔执行,比如:
DELIMITER $$
CREATE EVENT `jpa`.`create_sys_msg`
ON SCHEDULE
EVERY 1 MINUTE
comment '每分钟插入一条系统消息'
DO
BEGIN
INSERT INTO `jpa`.`sys_msg` (`msg`, `time`) VALUES ('hello', NOW());
END$$
DELIMITER ;
这个事件将在被创建后每分钟执行一次。
结果:
mysql> select * from sys_msg;
+----+-------+---------------------+
| id | msg | time |
+----+-------+---------------------+
| 4 | hello | 2023-07-11 16:16:57 |
| 5 | hello | 2023-07-11 16:17:57 |
| 6 | hello | 2023-07-11 16:18:57 |
+----+-------+---------------------+
有时候我们希望事件在固定时间点循环执行,比如每小时的0分、5分、15分这样(类似 Linux crontable 的*/5
)。这点可以通过使用STARTS
语句让事件在固定时间点开启来实现。
比如:
DELIMITER $$
CREATE EVENT `jpa`.`create_sys_msg`
ON SCHEDULE
EVERY 5 MINUTE
STARTS '2023-07-11 16:30:00'
COMMENT '每5分钟执行一次'
DO
BEGIN
INSERT INTO `jpa`.`sys_msg` (`msg`, `time`) VALUES ('hello', NOW());
END$$
DELIMITER ;
执行结果:
mysql> select * from sys_msg;
+----+-------+---------------------+
| id | msg | time |
+----+-------+---------------------+
| 11 | hello | 2023-07-11 16:30:00 |
| 12 | hello | 2023-07-11 16:35:00 |
+----+-------+---------------------+
STARTS
和ENDS
同样可以使用当前时间戳+时间段的方式指定,虽然这么做不太常见:
DELIMITER $$
CREATE EVENT `jpa`.`create_sys_msg`
ON SCHEDULE
EVERY 1 HOUR
STARTS CURRENT_TIMESTAMP+INTERVAL 1 DAY
ENDS CURRENT_TIMESTAMP+INTERVAL 3 DAY
COMMENT '每小时执行一次'
DO
BEGIN
INSERT INTO `jpa`.`sys_msg` (`msg`, `time`) VALUES ('hello', NOW());
END$$
DELIMITER ;
这个事件将在1天后的此时开始执行,并在3天后的此时结束执行。
用 SQLyog 执行修改事件操作,会出现类似下面的内容:
DELIMITER $$
ALTER DEFINER=`root`@`localhost` EVENT `create_sys_msg`
ON SCHEDULE EVERY 5 MINUTE
STARTS '2023-07-11 16:30:00'
ON COMPLETION NOT PRESERVE
ENABLE
COMMENT '每5分钟执行一次'
DO
BEGIN
INSERT INTO `jpa`.`sys_msg` (`msg`, `time`) VALUES ('hello', NOW());
END$$
DELIMITER ;
所以ALTER ... EVENT ...
语句可以用于修改事件。
可以直接用 SQLyog 删除选中事件,但也可以直接使用 SQL 语句删除:
我这版 SQLyog 将删除事件的选项翻译为"放置事件"。
DROP EVENT [IF EXISTS] event_name;
与触发器不同的是,在事件中可以直接调用存储过程,比如:
DELIMITER $$
CREATE EVENT `jpa`.`stat_student`
ON SCHEDULE
EVERY 10 MINUTE
DO
BEGIN
declare var_avg_score int;
declare var_sum_score int;
call showScoreAvg(0, 1, 10000, var_avg_score, var_sum_score);
insert into `jpa`.`student_stat` (`avg_score`,`total_score`,`time`) values(var_avg_score, var_sum_score,NOW());
END$$
DELIMITER ;
这里的存储过程示例showScoreAvg
来自这篇文章。
The End,谢谢阅读。