問題描述
如下圖中,GTID SET已經分成了無數段。正常情況下我們的從庫GTID SET應該是不會出現這種大量的空洞的,并且每次都丟失了一個GTID。如果這個時候重做從庫,那么會根據GTID的下限去主庫拿GTID信息,但是主庫先然已經清理了這些GTID信息必然會導致報錯。

問題定位與分析
其實導致出現gap與MySQL的bug有直接聯系
slave-skip-errors誘因
slave_skip_errors概述
mysql在主從復制過程中,由于各種的原因,從服務器可能會遇到執行BINLOG中的SQL出錯的情況,在默認情況下,服務器會停止復制進程,不再進行同步,等到用戶自行來處理。
slave-skip-errors的作用就是用來定義復制過程中從服務器可以自動跳過的錯誤號,當復制過程中遇到定義的錯誤號,就可以自動跳過,直接執行后面的SQL語句。
slave_skip_errors選項有四個可用值,分別為:
off,all,ErorCode,ddl_exist_errors
默認情況下該參數值是off,我們可以列出具體的error code,也可以選擇all
一些error code代表的錯誤如下:
1007: 數據庫已存在,創建數據庫失敗
1008: 數據庫不存在,刪除數據庫失敗
1050: 數據表已存在,創建數據表失敗
1051: 數據表不存在,刪除數據表失敗
1054: 字段不存在,或程序文件跟數據庫有沖突
1060: 字段重復,導致無法插入
1061: 重復鍵名
1068: 定義了多個主鍵
1094: 位置線程ID
1146: 數據表缺失,請恢復數據庫
1053: 復制過程中主服務器宕機
1062: 主鍵沖突 Duplicate entry '%s' for key %d
my.cnf中的寫法
slave_skip_errors=1062,1053
slave_skip_errors=all
slave_skip_errors=ddl_exist_errors
該參數為全局靜態參數,不能動態調整,可在my.cnf中加入該參數列表后重啟mysql服務器生效。
必須注意的是,啟動這個參數,如果處理不當,很可能造成主從數據庫的數據不同步,在應用中需要根據實際情況,如果對數據完整性要求不是很嚴格,那么這個選項確實可以減輕維護的成本。
slave-skip-errors=all導致的bug
這個bug存在于8.0.25最新版本(包括老版本),如果我們設置了slave-skip-errors=all參數后,遇到錯誤后DDL和DML對于GTID的處理不一致。可能導致slave重啟失敗。
DDL :跳過操作,跳過GTID。GTID 不連續。
DML:跳過操作,但是不跳過GTID。GTID 連續。
數據庫版本過低誘因
并沒有設置skip-slave-error參數,那么是其他什么BUG導致的呢?實際上這個BUG是5.7.23以下的版本,并且設置了replicate_wild_do_table等過濾規則會后對CREATE DATABASE/ALTER DATABASE/DROP DATABASE會過濾掉操作,并且從庫的GTID也會被拋棄掉,這樣就產生了大量的空洞。
測試驗證
選擇低于5.7.23的一個版本安裝主從復制
從庫配置表白名單
replicate_wild_do_table=test.t1
版本5.7.20(主庫)
Server version: 5.7.20-log MySQL Community Server (GPL)
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> create database test3;
Query OK, 1 row affected (0.00 sec)
mysql> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000002
Position: 1804
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 802e82b5-1efa-11ed-9196-5254000a56df:1-9
1 row in set (0.00 sec)
mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> insert into t1 values(3);
Query OK, 1 row affected (0.01 sec)
mysql> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000002
Position: 2057
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 802e82b5-1efa-11ed-9196-5254000a56df:1-10
1 row in set (0.00 sec)
從上面操作可以看到,主庫進行create database的操作gtid編號是9,insert到test.t1(也就是我們配置的表白名單)gtid編號是10
版本5.7.20(從庫)
mysql> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000003
Position: 600
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 802e82b5-1efa-11ed-9196-5254000a56df:1-3:7-8
1 row in set (0.00 sec)
mysql> show master status\G
*************************** 1. row ***************************
File: mysql-bin.000003
Position: 844
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 802e82b5-1efa-11ed-9196-5254000a56df:1-3:7-8:10
1 row in set (0.00 sec)
主庫信息

從庫信息

結論很明顯,GTID為9的事務被拋棄了,而insert t1的事務傳過來了。因此出現了gtid set的gap。
下面是官網提供的bug地址
https://bugs.mysql.com/bug.php?id=88891
https://bugs.mysql.com/bug.php?id=91086
問題處理
處理也很簡單,對于出現gap一般都是mysql本身的bug導致
(1)版本低于5.7.23,且需要主從配置replicate_wild_do_table,升級版本即可。
(2)如果版本高于5.7.23,則盡量避免配置slave_skip_errors=all的情況出現。
問題總結
mysql主從gtid一致,但從庫出現gtid set有gap的發生,一定要先檢查數據庫版本,盡量不使用低于5.7.23版本數據庫,對于高于5.7.23版本數據庫避免配置主從復制slave-skip-errors=all。




