MySQL5.1.6起增加了事件調(diào)度器(Event Scheduler),可用來(lái)做定時(shí)執(zhí)行某些特定任務(wù),用于取代原先只能由操作系統(tǒng)的計(jì)劃任務(wù)來(lái)執(zhí)行的工作。MySQL的事件調(diào)度器可以精確到每秒執(zhí)行一個(gè)任務(wù),而操作系統(tǒng)的計(jì)劃任務(wù)只能精確到分鐘級(jí)別。對(duì)于對(duì)數(shù)據(jù)實(shí)時(shí)性要求比較高的應(yīng)用非常合適。
事件調(diào)度器也稱(chēng)為臨時(shí)觸發(fā)器(Temporal Triggers),因?yàn)槭录{(diào)度器是基于特定時(shí)間周期觸發(fā)來(lái)執(zhí)行某些任務(wù),而觸發(fā)器(Triggers)是基于某個(gè)表所產(chǎn)生的事件觸發(fā)的。
MySQL定時(shí)任務(wù)的實(shí)現(xiàn)方式有兩種:
- 使用MySQL的
event
定時(shí)任務(wù)
使用MySQL的事件計(jì)劃,首先需要在服務(wù)器開(kāi)啟event_scheduler
后才能處理。 - 使用Linux的定時(shí)任務(wù)
crontab
如何開(kāi)啟事件計(jì)劃呢?
$ SHOW VARIABLES LIKE 'event_scheduler';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | OFF |
+-----------------+-------+
1 row in set
如果執(zhí)行命令后返回值為OFF
則表示目前事件計(jì)劃是處于關(guān)閉的狀態(tài)。
開(kāi)啟的方式也分為兩種,臨時(shí)方式使用命令行或腳本操作,永久修改則需要修改MySQL主配置文件my.ini
在其中添加event_schduler=1
的配置后重啟MySQL。
臨時(shí)性修改只要不重啟MySQL在當(dāng)前運(yùn)行狀態(tài)下會(huì)直接生效,一旦重啟后則失效。
$ SET GLOBAL event_scheduler = ON;
$ SET @@global.event_scheduler = ON;
$ SET GLOBAL event_scheduler = 1;
$ SET @@global.event_scheduler = 1;
事件調(diào)度器
要保證能夠執(zhí)行事件,就必須保證事件計(jì)劃是開(kāi)啟狀態(tài),事件計(jì)劃默認(rèn)為關(guān)閉狀態(tài)。
# 查看MySQL版本
$ SELECT VERSION();
+------------+
| VERSION() |
+------------+
| 5.7.18-log |
+------------+
1 row in set
# 事件計(jì)劃是否開(kāi)啟
$ SHOW VARIABLES LIKE 'event%'
$ SHOW VARIABLES LIKE 'event_scheduler';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | ON |
+-----------------+-------+
1 row in set
# 查看事件任務(wù)是否開(kāi)啟
$ SELECT @@event_scheduler;
+-------------------+
| @@event_scheduler |
+-------------------+
| ON |
+-------------------+
1 row in set
# 開(kāi)啟事件計(jì)劃
$ SET GLOBAL event_scheduler=1
$ SET GLOBAL event_scheduler=ON
duler=1;
Query OK, 0 rows affected
# 關(guān)閉事件計(jì)劃
$ SET GLOBAL event_scheduler=0
在真實(shí)開(kāi)發(fā)環(huán)境中會(huì)遇到MySQL服務(wù)重啟或斷電的情況,此時(shí)會(huì)出現(xiàn)事件調(diào)度器被關(guān)閉的情況。所有事件都不再起作用,解決的方式需要在MySQL的配置文件mysql.ini
中加入event_scheduler=ON
的配置。
事件任務(wù)
事件任務(wù)
# 查看事件任務(wù)
$ SHOW EVENTS;
Empty set
# 查看事件任務(wù)錯(cuò)誤 - 權(quán)限不足
$ SELECT * FROM mysql.event
1142 - SELECT command denied to user 'username'@'127.0.0.1' for table 'event'
# 開(kāi)啟事件任務(wù)
$ ALTER EVENT event_name ON COMPLETION PRESERVE ENABLE
# 關(guān)閉事件任務(wù)
$ ALTER EVENT event_name ON COMPLETION PRESERVE DISABLE
# 刪除事件
$ DROP EVENT [IF EXISTS] event_name
設(shè)置定時(shí)任務(wù)執(zhí)行SQL語(yǔ)句
例如:從當(dāng)日開(kāi)始每天凌晨4點(diǎn)刪除fight表超過(guò)一個(gè)月的數(shù)據(jù)
DROP EVENT IF EXISTS event_fight_delete;
CREATE EVENT event_fight_delete
ON SCHEDULE EVERY 1 DAY STARTS DATE_ADD(DATE(CURDATE() + 1), INTERVAL 4 HOUR)
DO
BEGIN
DELETE FROM center_fight WHERE 1=1 AND createdate < DATE_ADD(CURDATE(), INTERVAL -1 MONTH)
END
設(shè)置定時(shí)任務(wù)調(diào)用存儲(chǔ)過(guò)程
# 若計(jì)劃任務(wù)存在則刪除
DROP EVENT IF EXISTS event_name
# 創(chuàng)建計(jì)劃任務(wù)
CREATE EVENT event_name
ON SCHEDULE EVERY 10 second
STARTS TIMESTAMP '2018-07-12 00:00:00'
ON COMPLETION PRESERVE
DO
BEGIN
CALL producer()
END
參數(shù)說(shuō)明
-
ON SCHDULE schduler
定義執(zhí)行的時(shí)間和時(shí)間間隔 -
ON COMPLETION [NOT] PRESERVE
定義事件是一次性執(zhí)行還是永久執(zhí)行,默認(rèn)為一次性執(zhí)行,即NOT PRESERVE
。
在事件中ON SCHEDULE
計(jì)劃任務(wù)中有2種設(shè)定的方式
- 用來(lái)完成單次計(jì)劃任務(wù)。
AT 時(shí)間戳
eg:5天后
AT CURRENT_TIMESTAMP + INTERVAL 5 DAY
eg:某時(shí)間點(diǎn)
AT TIMESTAMP '2018-07-12 12:00:00'
- 用來(lái)完成重復(fù)的計(jì)劃任務(wù)
EVERY 時(shí)間(單位)的數(shù)量 時(shí)間單位 [STARTS 時(shí)間戳] [ENDS時(shí)間戳]
eg:每隔1秒
EVERY 1 SECOND
eg:每隔10分鐘。
EVERY 10 MINUTE
eg:從2018-08-01 12:00:00開(kāi)始每隔1天
EVERY 1 DAY STARTS '2018-08-01 12:00:00'
EVERY 10 second STARTS TIMESTAMP '2018-08-01 12:00:00'
eg:5天后開(kāi)啟每天定時(shí)處理
EVERY 1 DAY START CURRENT_TIMESTAMP + INTERVAL 5 DAY
eg:每天定時(shí)處理5天后停止
EVERY 1 DAY ENDS CURRENT_TIMESTAMP + INTERVAL 5 DAY
在兩種計(jì)劃任務(wù)中,時(shí)間戳可以是任意的TIMESTAMP
和DATETIME
數(shù)據(jù)類(lèi)型,時(shí)間戳需要大于當(dāng)前時(shí)間。
在重復(fù)的計(jì)劃任務(wù)中,時(shí)間(單位)的數(shù)量可以是任意非空(NOT NULL)的整數(shù)形式,時(shí)間單位是關(guān)鍵詞:YEAR
、MONTH
、DAY
、HOUR
、MINUTE
、SECOND
...
[ON COMPLETION [NOT] PRESERVE]
ON COMPLETION
參數(shù)表示“當(dāng)這個(gè)事件不會(huì)再發(fā)生的時(shí)候”,即當(dāng)單次計(jì)劃任務(wù)執(zhí)行完畢后或當(dāng)重復(fù)性的計(jì)劃任務(wù)執(zhí)行到了ENDS
階段。而PRESERVE
的作用是使事件在執(zhí)行完畢后不會(huì)被DROP
掉,建議使用該參數(shù),以便于查看EVENT
具體信息。
CREATE DEFINER=`root`@`localhost` EVENT `event_knapsacks_remember_expire`
ON SCHEDULE EVERY 1 MINUTE STARTS '2018-07-13 15:09:49'
ON COMPLETION PRESERVE ENABLE
COMMENT '每分鐘檢測(cè)背包中換牌卡到期并每日自動(dòng)減少'
DO
BEGIN
CALL produce_knapsacks_remember_expire();
END
存儲(chǔ)過(guò)程
DELIMITER $$
DROP PROCEDURE IF EXISTS procedure_name
CREATE PROCEDURE procedure_name()
BEGIN
INSERT INTO procedure_name(name, create_time) VALUES('name_value', now())
END $$
DELIMITER ;
-- 存儲(chǔ)過(guò)程 produce_knapsacks_remember_expire
-- 作用:判斷背包中道具記牌卡,是否過(guò)期,且每日減一。
CREATE DEFINER=`root`@`localhost` PROCEDURE `produce_knapsacks_remember_expire`()
BEGIN
DECLARE pk INT DEFAULT 0;
DECLARE sec INT DEFAULT 0;
DECLARE days INT DEFAULT 0;
DECLARE expire INT DEFAULT 0;
DECLARE mc CURSOR FOR (SELECT id,TIMESTAMPDIFF(SECOND,effect_time,NOW()) AS diff,TIMESTAMPDIFF(SECOND,NOW(),expire_time) AS expire FROM knapsacks WHERE name='REMEBER' AND expired=0);
OPEN mc;
ml:LOOP
FETCH mc INTO pk,sec,expire;
IF(expire <= 0) THEN
UPDATE `knapsacks` SET `expired`=1 WHERE `id`=pk;
ELSE
IF(sec>0 && sec<=86400) THEN
SET days = 1;
ELSEIF(sec>86400) THEN
SET days=CEILING(sec/86400);
END IF;
UPDATE `knapsacks` SET `quantity`=`purchase`-days,`consume`=days WHERE `id`=pk;
END IF;
COMMIT;
END LOOP ml;
CLOSE mc;
END
錯(cuò)誤處理
出現(xiàn)錯(cuò)誤
[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column 'information_schema.PROFILING.SEQ' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
解決方案
$ SELECT VERSION();
$ @@sql_mode;
$ SET sql_mode = (SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY' , ''));
出現(xiàn)提示
# 在SQL中查詢(xún)計(jì)劃事件的狀態(tài)
$ SHOW VARIABLES LIKE 'event_scheduler'
# 在mysql程序的目錄下找到my.ini文件添加
$ vim my.ini
event_scheduler = 1
# 保存后重啟mysql服務(wù)
# 用腳本來(lái)實(shí)現(xiàn)
# 開(kāi)啟event_scheduler sql指令:
SET GLOBAL event_scheduler = ON;
SET @@global.event_scheduler = ON;
SET GLOBAL event_scheduler = 1;
SET @@global.event_scheduler = 1;
$ 關(guān)閉event_scheduler指令:
SET GLOBAL event_scheduler = OFF;
SET @@global.event_scheduler = OFF;
SET GLOBAL event_scheduler = 0;
SET @@global.event_scheduler = 0;