MySQL8.0的參數event_scheduler默認是ON,請注意一些坑


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)

有沒有發現問題?

  1. 主從環境的event_scheduler都是開啟的。這意味這從庫有可能會重復寫入數據。一部分數據來源於主庫的event的循環插入的復制,一部分數據來源於從庫自身的event的循環插入。

  2. 主從庫的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讓其可以跑。這也屬於一個坑吧。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM