event_scheduler是什么?
event_scheduler是什么MySQL定時器的開關,類似於windows操作系統的定時任務的概念,指定某個時間點執行一次定時任務,或者每隔一段時間循環執行定時任務。
這個東西有企業在用么?
看了幾個企業的開發規范,都沒有提及需要禁用event功能,所以event功能是允許開發人員使用的。(不過,我相信java開發人員更喜歡用java定時器。)也許是因為mysql默認就不啟用event功能嘛,默認就無法用event,開發規范就不提及這東西啦。況且可以通過用戶權限控制來控制開發人員是否可以使用event。所以從我手上的幾家公司的開發規范里,並不可以實際看出大家有沒有使用event_scheduler這個功能。由於MySQL8.0默認是開啟event_scheduler功能了,我認為我們還是有必要討論一下event到底有沒有坑?
我能想到的坑——主從復制的坑
首先這個事情並沒有實際發生在我生產環境,是人為想出來的一種場景。
准備:
兩個MySQL8.0數據庫實例
mysql3308
mysql33081
配置文件不顯示地指定默認值:
[root@192-168-199-198 mysql3308]# cat my.cnf |grep event_scheduler
MySQL8.0的默認值即為:
mysql> show variables like '%event_scheduler%';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | ON |
+-----------------+-------+
1 row in set (0.12 sec)
我給大家做了以下的測試。
一、在已有的主從環境下,建立一個重復插入的event。
主庫上執行
mysql> show variables like '%event_scheduler%';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | ON |
+-----------------+-------+
1 row in set (0.12 sec)
create database fander;
use fander;
CREATE EVENT IF NOT EXISTS test
ON SCHEDULE EVERY 1 SECOND
ON COMPLETION PRESERVE
DO insert into fander.test values (1);
#這個event表示每一秒都往fander庫的test表插入一行值為1的數據。
mysql> show create event test\G
*************************** 1. row ***************************
Event: test
sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
time_zone: SYSTEM
Create Event: CREATE DEFINER=`root`@`localhost` EVENT `test` ON SCHEDULE EVERY 1 SECOND STARTS '2019-01-23 14:55:19' ON COMPLETION PRESERVE ENABLE DO insert into fander.test values (1)
character_set_client: utf8mb4
collation_connection: utf8mb4_0900_ai_ci
Database Collation: utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
mysql> show tables;
Empty set (0.01 sec)
#我先不建立測試重復插入的test表。
從庫上執行
mysql> show variables like '%event_scheduler%';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | ON |
+-----------------+-------+
1 row in set (0.12 sec)
mysql> use fander;
mysql> show create event test\G
*************************** 1. row ***************************
Event: test
sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
time_zone: SYSTEM
Create Event: CREATE DEFINER=`root`@`localhost` EVENT `test` ON SCHEDULE EVERY 1 SECOND STARTS '2019-01-23 14:55:19' ON COMPLETION PRESERVE DISABLE ON SLAVE DO insert into fander.test values (1)
character_set_client: utf8mb4
collation_connection: utf8mb4_0900_ai_ci
Database Collation: utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
有沒有發現問題?
-
主從環境的event_scheduler都是開啟的。這意味這從庫有可能會重復寫入數據。一部分數據來源於主庫的event的循環插入的復制,一部分數據來源於從庫自身的event的循環插入。
-
主從庫的Create Event語句不一樣?
在create event時,event在主庫創建后,復制到從庫上是disable的狀態!所以MySQL在設計之初就考慮了這個問題。event在從庫是disable的,無法執行,從而從庫不會發生寫入操作而導致數據不一致!
我們進一步測試,是不是確實如何此。
主庫上執行
mysql> create table test (a int);
Query OK, 0 rows affected (0.04 sec)
mysql> select count(1) from test;
+----------+
| count(1) |
+----------+
| 52 |
+----------+
1 row in set (0.01 sec)
從庫上執行
mysql> select count(1) from test;
+----------+
| count(1) |
+----------+
| 52 |
+----------+
1 row in set (0.01 sec)
#注意,也可以用對比gtid的方法查看從庫,發現從庫確實沒有自身數據寫入。
結論是,在已有的主從環境下,建立一個重復插入的event,event不會在從庫上執行,不會有數據寫入,從而保證了數據的一致性。這種情況下event的開啟沒有帶來坑。
二、在已有一個重復插入的event的主庫上,擴展建立一個從庫。
主庫:
mysql> show variables like '%event_scheduler%';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | ON |
+-----------------+-------+
1 row in set (0.12 sec)
mysql> show create event test\G
*************************** 1. row ***************************
Event: test
sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
time_zone: SYSTEM
Create Event: CREATE DEFINER=`root`@`localhost` EVENT `test` ON SCHEDULE EVERY 1 SECOND STARTS '2019-01-23 14:55:19' ON COMPLETION PRESERVE ENABLE DO insert into fander.test values (1)
character_set_client: utf8mb4
collation_connection: utf8mb4_0900_ai_ci
Database Collation: utf8mb4_0900_ai_ci
1 row in set (0.06 sec)
冷備的方法建立從庫。
mysql> show variables like '%event_scheduler%';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| event_scheduler | ON |
+-----------------+-------+
1 row in set (0.12 sec)
mysql> CHANGE MASTER TO
-> MASTER_HOST='localhost',
-> MASTER_USER='rpl_user',
-> MASTER_PASSWORD='password',
-> MASTER_PORT=3308,
-> master_auto_position=1;
Query OK, 0 rows affected, 2 warnings (0.04 sec)
mysql> start slave;
Query OK, 0 rows affected (0.02 sec)
mysql> use fander
Database changed
mysql> show create event test\G
*************************** 1. row ***************************
Event: test
sql_mode: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
time_zone: SYSTEM
Create Event: CREATE DEFINER=`root`@`localhost` EVENT `test` ON SCHEDULE EVERY 1 SECOND STARTS '2019-01-23 14:55:19' ON COMPLETION PRESERVE ENABLE DO insert into fander.test values (1)
character_set_client: utf8mb4
collation_connection: utf8mb4_0900_ai_ci
Database Collation: utf8mb4_0900_ai_ci
1 row in set (0.00 sec)
可以發現主從庫的Create Event語句這時是一模一樣了。
下面接着測試。
主庫:
mysql> select count(1) from test;
+----------+
| count(1) |
+----------+
| 14 |
+----------+
1 row in set (0.00 sec)
從庫:
mysql> select count(1) from test;
+----------+
| count(1) |
+----------+
| 33 |
+----------+
1 row in set (0.00 sec)
mysql> show slave status\G
...
Retrieved_Gtid_Set: 07b92486-64b0-11e8-b4cf-000c29c71881:501-561
Executed_Gtid_Set: 07b92486-64b0-11e8-b4cf-000c29c71881:1-561,
59613f3a-1ee0-11e9-a9e7-000c29259487:1-13
...
以上測試用的是物理冷備。我測試在用邏輯備份主庫,擴展從庫后,情況一樣。
結論是,在已有一個重復插入的event的主庫上,擴展建立一個從庫,會因為從庫也有這個event schedule,導致從庫重復執行insert語句。等於計划任務重新執行了兩次!數據會造成不一致!
如何避免?
建議:
1.規范my.cnf模板,event_scheduler設置為0,而不是采用官方默認值1。
2.從庫建議開啟read_only=1; 嚴格防止寫入。
擴展
如果日常備份時備份的是文章"一"情況的從庫,那么恢復數據庫后,event默認是disable的,是跑不了的。具體見下面:
[root@192-168-199-198 ~]# cat all2.sql |grep "EVERY 1 SECOND"
/*!50106 CREATE*/ /*!50117 DEFINER=`root`@`localhost`*/ /*!50106 EVENT `test` ON SCHEDULE EVERY 1 SECOND STARTS '2019-01-23 16:03:16' ON COMPLETION PRESERVE DISABLE ON SLAVE DO insert into fander.test values (1) */ ;;
你需要人手修改event讓其可以跑。這也屬於一個坑吧。