GTID工具聯動:http://blog.itpub.net/29510932/viewspace-1736132/
-------------------------------------------------------------------------------------------------正文---------------------------------------------------------------------------------------------------------------
場景:
MySQL-5.7.12, 開啟GTID, M1和M2雙主同步, S1從庫, RC隔離級別
背景:
因為網絡波動導致S1的Master從M1切換到了M2, 切換過去以后同步失敗, 報錯信息如下:
Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.'
分析:
從報錯信息上很明顯能看出是M2主庫purge掉了S1從庫還沒有receive的事務, 所以報錯了;
着手處理:
不過印象中這個業務庫本身應該是沒什么業務流量的, 不至於需要purge掉binlog;
遂登錄主庫M2看一下:
binlog日志都在.......
那么就很奇怪了....M2的binlog沒有purge過, 為什么會報這個ER_MASTER_HAS_PURGED_REQUIRED_GTIDS的錯誤呢?
考慮到時間緊迫, 臨時用log_file和position的方式恢復了M2與S1的同步;
確認沒有再出現問題以后, google+翻了一下源代碼;
根據關鍵字ER_MASTER_HAS_PURGED_REQUIRED_GTIDS找到~/mysql/sql/rpl_binlog_sender.cc的第744行,
點擊(此處)折疊或打開
- if (!gtid_state->get_lost_gtids()->is_subset(m_exclude_gtid))
- {
- errmsg= ER(ER_MASTER_HAS_PURGED_REQUIRED_GTIDS);
- global_sid_lock->unlock();
- set_fatal_error(errmsg);
- return 1;
- }
結合一部分注釋, 以及代碼上下文的內容, 可以知道M2在初始化dump線程的時候, 會檢查S1的一些GTID相關的狀態,
這段代碼會檢查從庫是否能"繼續"從主庫同步事務;
判斷的條件可以簡單描述為:主庫不能purge掉從庫還沒有execute的事務(或者是主庫壓根就沒有那一部分日志);
一直到這里, 都證明了最初的判斷是沒問題的, 那為什么M2的binlog全部都在, 但是S1又報這個錯誤了?
登錄M2, 看一下GTID_Purged的信息,
居然還真的有purged的信息, 不過這個UUID並不是M2的, 而是M1的;
在replication中, 會有這么一種現象:通過replication獲得的事務,在SQL thread復現完以后, 會自動purge掉;
所以在M2上面, 就出現了M1的GTID被purge的信息;
由於M1和M2並沒有開啟slave-log-update, 所以S1在以M2為主庫的時候,無法獲取到M1的事務,
而切換的過程中, S1斷開了與M1的連接, 在建立起與M2的連接前, M2復現了一些M1的事務, 並Purge掉了;
當S1的master切換到M2以后, 在dump前, 會檢查到M2上9cfe6b63-3e90-11e6-a1db-525400e9a4af:1-66177的事務都全部purge掉了,
所以S1連接到M2之后,發現S1的executed_gtid中, 9cfe6b63-3e90-11e6-a1db-525400e9a4af的事務低於66177(憑記憶, 報錯的時候是33000多,估計是因為網絡的波動的原因, 同步中斷了有一陣子了), 所以報出了之前的錯誤:master has purged binary logs;
之后在測試環境搭建了一套測試環境, 在Master的GTID_Purged超過Slave的Executd_GTID時, 必定會報出這個錯誤, 不論這個GTID的UUID是不是Master自己的;
總結:
所以在這個案例中,最合適的做法是在S1上面Purge掉主庫已經Purged的事務, 然后再使用auto_position=1的方式進行同步;
以后再遇到類似的問題, 也可以遵循一個基本的原則來判斷是需要purge, 還是跳過:
從庫在開始同步前,主庫會依靠GTID來確認從庫在開始同步以后, 能夠把每一個主庫上執行過的事務(包括slave的SQL Thread)都復現一次,最終保持和主庫完全一致;
判斷方法也很簡單,基本基於兩個條件:
1.主庫不能purge從庫還沒有execute的事務(即從庫的executed_GTID要大於主庫的GTID_Purged);
2.主庫上的事務號不能低於從庫(即從庫的executed_GTID的最后一個事務要在主庫的executed_GTID的范圍之內);
PS: 在這個案例中, 由於從庫已經獲取不到從33000多到66177的事務了(主庫GTID_Purged中的記錄), 所以不符合條件1終止同步過程, 並報錯;
