概括
给定一个调用存储过程的事件,确保一次只运行该过程的一个实例的最佳实践是什么?特别是在程序运行时间有时可能比事件结束时间更长的情况下。
例子
让我们看下面这个虚构的例子,一个需要1秒才能结束的事件和一个需要5秒才能执行的过程:
程序:
DELIMITER ;;
CREATE PROCEDURE `P_wait`()
BEGIN
SELECT SLEEP(5);
END;;
DELIMITER ;
事件:
DROP EVENT IF EXISTS `E_wait`;
DELIMITER ;;
CREATE EVENT `E_wait`
ON SCHEDULE
EVERY 1 SECOND
DO
BEGIN
CALL `P_wait`(); //proc_call
END;;
DELIMITER ;
如您所料,当事件运行时,您将SLEEP()
在 中看到 5 个实例PROCESSLIST
:
mysql> SHOW PROCESSLIST;
+-------+------+-----------+-------+---------+------+-------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-------+------+-----------+-------+---------+------+-------------+------------------+
| 27045 | root | localhost | temp | Query | 0 | NULL | SHOW PROCESSLIST |
| 27069 | root | localhost | temp | Connect | 4 | User sleep | SELECT SLEEP(5) |
| 27070 | root | localhost | temp | Connect | 3 | User sleep | SELECT SLEEP(5) |
| 27072 | root | localhost | temp | Connect | 2 | User sleep | SELECT SLEEP(5) |
| 27073 | root | localhost | temp | Connect | 1 | User sleep | SELECT SLEEP(5) |
| 27074 | root | localhost | temp | Connect | 0 | User sleep | SELECT SLEEP(5) |
+-------+------+-----------+-------+---------+------+-------------+------------------+
虽然在此示例中并非如此,但您可以看到如果过程被阻塞(例如,如果它包含带有SELECT ... FOR UPDATE
语句的事务),这将迅速升级为问题。
问题
确保当E_wait
每秒滴答声时,如果一个实例P_wait
仍在运行,它不会再次被调用的最佳方法是什么?我一次最多只想要一个P_wait
运行实例。这里的最佳实践是什么?
/*PROCEDURE_NOT_RUNNING*/
基本上,如果我按如下方式修改事件,我需要代替什么:
...
BEGIN
IF /*PROCEDURE_NOT_RUNNING*/ THEN
CALL wait_test(); //proc_call
END IF;
END;;
...
- 从过程中更改事件
E_wait
(在开始时禁用P_wait
并在结束时重新启用)不是一种选择,因为如果服务器在禁用时重新启动,这可能导致事件根本不运行。 - 我可以尝试将一个值存储在一个表中并使用它来确定该过程是否仍在运行,但这似乎存在相同的问题,即如果服务器在错误的时间停止运行,则可能会锁定。
- 本次讨论的重点是该过程可能需要任意长的时间才能执行,因此仅将事件时间表更改为 5 秒是不可接受的:P
期望的结果
要运行上述事件,并且最多只能看到一个SELECT SLEEP(5)
in实例SHOW PROCESSLIST
。