使用事件调度程序
MySQL事件调度程序管理事件的调度和执行,即根据调度运行的任务。下面的讨论涵盖了事件调度程序,并分为以下几个部分:
事件调度程序概述
MySQL事件是根据计划运行的任务。因此,我们有时将它们称为计划事件。当您创建事件时,您正在创建一个命名的数据库对象,其中包含一个 或多个SQL语句,这些SQL语句将以一个或多个常规间隔执行,开始和结束于特定的日期和时间。从概念上讲,这类似于Unix crontab(也称为“ cron job”)或Windows Task Scheduler的思想
这种类型的计划任务有时也称为“时间触发器”,这意味着这些对象是由时间推移触发的。虽然这基本上是正确的,但我们倾向于使用事件这个 术语,以避免与触发器类型混淆。更确切地说,事件不应与“临时触发器”混淆。触发器是一个数据库对象,其语句是为了响应在给定表上发生 的特定类型的事件而执行的,而(预定)事件是一个对象,其语句是为了响应指定时间间隔的通过而执行的。
虽然SQL标准中没有事件调度的规定,但在其他数据库系统中有先例,您可能会注意到这些实现与MySQL服务器中的实现之间存在一些相似之处。
MySQL事件有以下主要特性和属性:
.在MySQL中,一个事件是由它的名称和它所分配的模式唯一标识的。
.事件根据时间表执行特定的操作。这个操作由一条SQL语句组成,它可以是BEGIN…END语句块。事件发生的时间可以是一次性的,也可以是周期 性的。一次性事件只执行一次。周期性事件以固定的时间间隔重复其操作,可以为周期性事件分配特定的开始日和时间、结束日和时间,或两者 都指定,或两者都不指定。(默认情况下,一个循环事件的时间表在创建时就开始了,并无限期地持续下去,直到它被禁用或删除。)
如果重复事件在其调度间隔内未终止,则结果可能是该事件的多个实例同时执行。如果不希望出现这种情况,应该建立一种机制来防止同时多实 例执行。例如,可以使用GET_LOCK()函数,或者行锁或表锁。
.用户可以使用专门用于这些目的的 SQL 语句创建、修改和删除计划事件。语法无效的事件创建和修改语句会以适当的错误消息失败。用户可以 在事件的操作中包含需要其实际上并不具备的权限的语句。事件创建或修改语句会成功,但事件的操作会失败。
.事件的许多属性可以使用SQL语句设置或修改。这些属性包括事件的名称、时间、持久性(即是否在其调度过期后保留)、状态(启用或禁用) 、要执行的操作以及分配给它的模式。
事件的默认定义者是创建该事件的用户,除非该事件已被更改,在这种情况下,定义者是发出影响该事件的最后alter event语句的用户。在为 其定义事件的数据库上具有event特权的任何用户都可以修改事件。
.事件的操作语句可以包含存储例程中允许的大多数SQL语句。
事件调度程序配置
事件由一个特殊的事件调度程序线程执行;当我们引用事件调度程序时,我们实际上引用的是这个线程。运行时,具有PROCESS特权的用户可以 在SHOW PROCESSLIST的输出中看到事件调度器线程及其当前状态,如下面的讨论所示。
全局event_scheduler系统变量决定事件调度程序是否已启用并在服务器上运行。它有以下3个值之一,影响事件调度,如下所述:
.OFF:事件调度程序已停止。事件调度器线程没有运行,没有显示在SHOW PROCESSLIST的输出中,也没有执行计划的事件。OFF是 event_scheduler的默认值。
当事件调度程序停止时(event_scheduler为OFF),可以通过将event_scheduler的值设置为ON来启动它。(见下一条。)
.ON:事件调度程序已启动;事件调度程序线程运行并执行所有计划的事件。
当事件调度器打开时,事件调度器线程会作为守护进程在SHOW PROCESSLIST的输出中列出,其状态如下所示:
mysql> SHOW PROCESSLIST\G
*************************** 1. row ***************************
Id: 166
User: root
Host: mysqlcs:41344
db: test
Command: Query
Time: 0
State: starting
Info: SHOW PROCESSLIST
1 row in set (0.01 sec)
mysql> select @@event_scheduler;
+-------------------+
| @@event_scheduler |
+-------------------+
| OFF |
+-------------------+
1 row in set (0.00 sec)
mysql> set global event_scheduler=ON;
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW PROCESSLIST\G
*************************** 1. row ***************************
Id: 166
User: root
Host: mysqlcs:41344
db: test
Command: Query
Time: 0
State: starting
Info: SHOW PROCESSLIST
*************************** 2. row ***************************
Id: 167
User: event_scheduler
Host: localhost
db: NULL
Command: Daemon
Time: 7
State: Waiting on empty queue
Info: NULL
2 rows in set (0.00 sec)
可以通过将event_scheduler的值设置为OFF来停止事件调度。
.DISABLED:此值使事件调度程序不可操作。事件调度程序为DISABLED时,事件调度器线程不会运行(因此不会出现在SHOW PROCESSLIST的输出 中)。此外,事件调度程序状态不能在运行时更改。
如果事件调度器的状态没有设置为DISABLED, event_scheduler可以在ON和OFF之间切换(使用set)。在设置该变量时,也可以使用0表示关闭 ,1表示开启。因此,以下4条语句中的任何一条都可以在mysql客户端中开启事件调度器:
SET GLOBAL event_scheduler = ON; SET @@global.event_scheduler = ON; SET GLOBAL event_scheduler = 1; SET @@global.event_scheduler = 1;
类似地,下面这4个语句中的任何一个都可以用来关闭事件调度器:
SET GLOBAL event_scheduler = OFF; SET @@global.event_scheduler = OFF; SET GLOBAL event_scheduler = 0; SET @@global.event_scheduler = 0;
尽管ON和OFF具有等效的数值,但SELECT或SHOW VARIABLES总是OFF、ON或DISABLED中的一个。DISABLED没有相应的数值。由于这个原因,在设置 这个变量时,ON和OFF通常比1和0更可取。
注意,尝试设置event_scheduler而不将其指定为全局变量会导致错误:
mysql> SET @@event_scheduler = OFF; ERROR 1229 (HY000): Variable 'event_scheduler' is a GLOBAL variable and should be set with SET GLOBAL
只有在服务器启动时才可以将事件调度程序设置为DISABLED。如果event_scheduler是ON或OFF,您不能在运行时将其设置为DISABLED。另外,如 果在启动时将事件调度程序设置为DISABLED,则不能在运行时更改event_scheduler的值。
要禁用事件调度程序,请使用以下两种方法之一:
.作为启动服务器时的命令行选项:
–event-scheduler=DISABLED
.在服务器配置文件(my.cnf,或Windows系统上的my.ini)中,包括将被服务器读取的行(例如,在[mysqld]部分中):
event_scheduler=DISABLED
要启用事件调度器,可以在不使用–event-scheduler =DISABLED命令行选项的情况下重启服务器,或者在服务器配置文件中删除或注释掉包含 event-scheduler= DISABLED的行之后再重启服务器。或者,您可以在启动服务器时使用ON(或1)或OFF(或0)代替DISABLED值。
当event_scheduler设置为DISABLED时,可以发出事件操作语句。在这种情况下,不会生成警告或错误(前提是语句本身有效)。但是,在将该 变量设置为ON(或1)之前,计划的事件无法执行。完成此操作后,事件调度程序线程执行满足调度条件的所有事件。
使用—–skip-grant-tables选项启动MySQL服务器会导致event_scheduler被设置为DISABLED,覆盖命令行或my.cnf或my.ini文件中设置的任何 其他值(Bug# 26807)。
MySQL在INFORMATION_SCHEMA数据库中提供了一个EVENTS表。通过查询该表,可以获取服务器上已定义的定时事件信息。
事件的语法
MySQL提供了几个SQL语句来处理计划事件:
.使用CREATE EVENT语句定义新事件。
.现有事件的定义可以通过ALTER event语句进行更改。
.当不再需要或不再需要计划的事件时,它的定义器可以使用DROP EVENT语句从服务器中删除它。事件是否持续到其调度结束后,还取决于它的 on COMPLETION子句(如果有的话)。
对定义事件的数据库具有事件权限的任何用户都可以删除事件。
事件元数据
事件元数据获取方式如下:
.查询mysql数据库event表。
.查询INFORMATION_SCHEMA数据库的EVENTS表。
.使用SHOW CREATE EVENT语句。
.使用SHOW EVENTS语句。
事件调度程序时间表示
MySQL中的每个会话都有一个会话时区(STZ)。这是会话time_zone值,在会话开始时从服务器的全局time_zone值初始化,但可以在会话期间更 改。
执行CREATE EVENT或ALTER EVENT语句时当前的会话时区用于解释事件定义中指定的时间。这就变成了事件时区(ETZ);也就是说,用于事件调 度并在事件执行时在事件内生效的时区。
用于在mysql.event表中表示事件信息。execute_at、starts和ends时间被转换为UTC,并与事件时区一起存储。这使得事件执行可以按照定义进 行,而不需要考虑对服务器时区或夏令时效果的任何后续更改。上一次执行时间也存储在UTC中。
如果您从 `mysql.event` 中选择信息,刚才提到的时间将作为 UTC 值检索。这些时间也可以通过从 `INFORMATION_SCHEMA.EVENTS` 表中选择 或使用 `SHOW EVENTS` 获取,但它们会以 ETZ 值的形式报告。从这些来源获取的其他时间表明事件创建或最后修改的时间;这些会以 STZ 值 的形式显示。
事件调度程序状态
事件调度程序将有关事件执行的信息写入MySQL服务器的错误日志,并以错误或警告结束。
要获取事件调度程序的状态信息以进行调试和故障排除,请运行mysqladmin debug;运行此命令后,服务器的错误日志包含与事件调度程序相关 的输出,类似于以下所示:
Events status: LLA = Last Locked At LUA = Last Unlocked At WOC = Waiting On Condition DL = Data Locked Event scheduler status: State : INITIALIZED Thread id : 0 LLA : init_scheduler:313 LUA : init_scheduler:318 WOC : NO Workers : 0 Executed : 0 Data locked: NO Event queue status: Element count : 1 Data locked : NO Attempting lock : NO LLA : init_queue:148 LUA : init_queue:168 WOC : NO Next activation : 0000-00-00 00:00:00
在作为事件调度器执行的事件的一部分发生的语句中,诊断消息(不仅是错误,还有警告)被写入错误日志,在Windows上,则写入应用程序的 事件日志。对于频繁执行的事件,可能会产生很多日志消息。例如,对于SELECT … INTO var_list语句,如果查询没有返回任何行,则会出现 错误码为1329的警告(没有数据),并且变量值保持不变。如果查询返回多行,则会发生1172错误(Result包含多行)。对于任何一种情况,你 都可以通过声明一个条件处理程序来避免记录警告;
对于可能检索多行的语句,另一种策略是使用LIMIT 1将结果集限制为单行。
事件调度程序和MySQL特权
要启用或禁用计划事件的执行,必须设置全局event_scheduler系统变量的值。这需要SUPER特权。
EVENT 权限控制事件的创建、修改和删除。可以使用 GRANT 语句授予此权限。例如,以下 GRANT 语句将名为 myschema 的模式上的 EVENT 权 限授予用户 jon@ghidora:
GRANT EVENT ON myschema.* TO jon@ghidora;
我们假设这个用户帐户已经存在,并且我们希望它保持不变。
要在所有模式上为同一个用户授予EVENT特权,使用下面的语句:
GRANT EVENT ON *.* TO jon@ghidora;
EVENT特权具有全局或模式级作用域。因此,尝试在单个表上授予它会导致如下所示的错误:
mysql> GRANT EVENT ON myschema.mytable TO jon@ghidora; ERROR 1144 (42000): Illegal GRANT/REVOKE command; please consult the manual to see which privileges can be used
重要的是要理解,事件是用其定义器的权限执行的,它不能执行其定义器没有必要权限的任何操作。例如,假设jon@ghidora具有myschema的 EVENT特权。假设这个用户对myschema有SELECT权限,但对这个模式没有其他权限。jon@ghidora可以创建一个新事件,如下所示:
CREATE EVENT e_store_ts ON SCHEDULE EVERY 10 SECOND DO INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP());
用户等待一分钟左右,然后执行SELECT * FROM mytable;查询,期望在表中看到几个新行。相反,这个表是空的。由于用户对所讨论的表没有 INSERT权限,因此该事件没有影响。
如果你检查MySQL错误日志(hostname.err),你可以看到事件正在执行,但是它试图执行的动作失败了:
2025-09-24T12:41:31.261992Z 25 [ERROR] Event Scheduler: [jon@ghidora][cookbook.e_store_ts] INSERT command denied to user 'jon'@'ghidora' for table 'mytable' 2025-09-24T12:41:31.262022Z 25 [Note] Event Scheduler: [jon@ghidora].[myschema.e_store_ts] event execution failed. 2025-09-24T12:41:41.271796Z 26 [ERROR] Event Scheduler: [jon@ghidora][cookbook.e_store_ts] INSERT command denied to user 'jon'@'ghidora' for table 'mytable' 2025-09-24T12:41:41.272761Z 26 [Note] Event Scheduler: [jon@ghidora].[myschema.e_store_ts] event execution failed.
由于该用户很可能没有访问错误日志的权限,因此可以通过直接执行事件的action语句来验证事件的action语句是否有效:
检查INFORMATION_SCHEMA.EVENTS表显示e_store_ts存在并且被启用,但是它的LAST_EXECUTED列为NULL:
mysql> SELECT * FROM INFORMATION_SCHEMA.EVENTS > WHERE EVENT_NAME='e_store_ts' > AND EVENT_SCHEMA='myschema'\G *************************** 1. row *************************** EVENT_CATALOG: NULL EVENT_SCHEMA: myschema EVENT_NAME: e_store_ts DEFINER: jon@ghidora EVENT_BODY: SQL EVENT_DEFINITION: INSERT INTO myschema.mytable VALUES (UNIX_TIMESTAMP()) EVENT_TYPE: RECURRING EXECUTE_AT: NULL INTERVAL_VALUE: 5 INTERVAL_FIELD: SECOND SQL_MODE: NULL STARTS: 0000-00-00 00:00:00 ENDS: 0000-00-00 00:00:00 STATUS: ENABLED ON_COMPLETION: NOT PRESERVE CREATED: 2006-02-09 22:36:06 LAST_ALTERED: 2006-02-09 22:36:06 LAST_EXECUTED: NULL EVENT_COMMENT: 1 row in set (0.00 sec)
要撤销EVENT特权,使用REVOKE语句。在这个例子中,模式myschema上的EVENT特权从jon@ghidora用户帐户中移除:
REVOKE EVENT ON myschema.* FROM jon@ghidora;
撤销用户的EVENT权限不会删除或禁用该用户可能创建的任何事件。
事件不会因为重命名或删除创建事件的用户而被迁移或删除。
假设用户jon@ghidora被授予了myschema模式上的EVENT和INSERT权限。然后,该用户创建以下事件:
CREATE EVENT e_insert ON SCHEDULE EVERY 7 SECOND DO INSERT INTO myschema.mytable;
创建此事件后,root将撤销jon@ghidora的event权限。然而,e_insert继续执行,每7秒向mytable中插入一行。如果root发出了以下语句中的任 何一个,也会产生相同的结果:
.DROP USER jon@ghidora; .RENAME USER jon@ghidora TO someotherguy@ghidora;
您可以在发出DROP USER或RENAME USER语句之前和之后通过检查mysql.event表或INFORMATION_SCHEMA。EVENTS表来验证这是正确的。
事件定义存储在mysql.event表。要删除由其他用户帐户创建的事件,MySQL root用户(或其他具有必要权限的用户)可以从该表中删除行。例 如,要删除前面显示的e_insert事件,root用户可以使用以下语句:
DELETE FROM mysql.event WHERE db = 'myschema' AND definer = 'jon@ghidora' AND name = 'e_insert';
从mysql.event表中删除行时,匹配事件名称、数据库模式名称和用户帐户是非常重要的。。这是因为相同的用户可以在不同的模式中创建相同 名称的不同事件。
用户的“EVENT”权限存储在“mysql.user”和“mysql.db”表的“Event_priv”列中。在这两种情况下,该列都只包含“Y”或“N”这两个值 之一。“N”是默认值。“mysql.user.Event_priv”仅在给定用户具有全局“EVENT”权限时(即使用“GRANT EVENT ON *.*”授予权限)才被 设置为“Y”。对于模式级别的“EVENT”权限,“GRANT”会在“mysql.db”中创建一行,并将该行的“Db”列设置为模式名称,“User”列设 置为用户名称,“Event_priv”列设置为“Y”。通常情况下,绝不需要直接操作这些表,因为“GRANT EVENT”和“REVOKE EVENT”语句会执行 对它们的所需操作。
五个状态变量提供与事件相关的操作计数。如下所示:
.Com_create_event:自上次服务器重启以来执行的CREATE EVENT语句的数量。
.Com_alter_event:自上次服务器重启以来执行的ALTER EVENT语句的数量。
.Com_drop_event:自上次服务器重启以来执行的DROP EVENT语句的数量。
.Com_show_create_event:自上次服务器重启以来执行的SHOW CREATE EVENT语句的个数。
.Com_show_events:自上次服务器重启以来执行的SHOW EVENTS语句的个数。
您可以通过运行SHOW STATUS LIKE语句一次查看所有这些的当前值;“%event%”。