一、背景
GTID的原理這篇文章不再展開,有興趣的同學可以關注之前的GTID原理,GTID實戰,GTID運維實戰文章。
如果每個執行個體的GTID相同,那麼可以大機率說明資料的一緻性。
是以,我們要保證slave的GTID一定是master的子集,因為基于複制原理,slave一般是延後master的。
于是,我們就實作了一個監控,如果slave不是master的子集,那麼告警出來,截圖如下:
上圖列出的GTID就是有問題的,不是master的子集。
一開始,這麼做主要是處于自己的潔癖,以及對規範的強要求和依賴。
後來有好多小朋友跟我說,這個監控沒有任何意義:
1) slave切換下,就不一緻了
2) 即便不是子集,在slave進行了操作,比如:flush 等操作,隻要不影響資料一緻性,也沒關系的
balabala好多類似的理由。
當時,我也沒有太好的利用說服,隻能自己負責的業務默默遵從。
後來再仔細想想GTID的原理,結合實戰,對這個監控有了新的認識
二、故障複現和原理剖析
- 先簡單說說結論:
如果candidate master的非子集GTID對應的binlog日志被purge了,那麼MHA切換的時候,會導緻從庫IO線程失敗。
報錯如下:
Last_IO_Errno: 1236
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.'
- 故障複現的步驟:
- candidate master: flush slow logs; --産生一些非子集的gtid event
- candidate master: purge binary logs to xx --将剛剛産生的非子集gtid所在的binlog給删除掉
- master : 模拟切換
- 報錯産生
- 原理剖析:
a) 當備選master晉升為new master時,其他的執行個體會擷取cm_uuid:1這個gtid
b)如果cm_uuid:1 已經被purge了,那麼就會報錯 。
- 回到開頭,為什麼說這個監控價值一個億呢?
- 如果slave沒有業務,其實問題不大。
- 如果slave 有業務呢,現在很多架構是讀寫分離的,如果不能及時修複主從關系,那麼延遲的資料造成的損失就不能簡簡單單的錢來衡量了。
三、解決方案
- 方案其實很簡單: 巡檢出問題,修複問題,最終一定要保證slave是master的子集。
- 如果修複gtid呢:如果确定slave上的gtid不影響資料的一緻性,那麼可以手動reset gtid來修複即可。
四、Q&A
Q1: 通過在slave 設定 read_only 可以避免吧。
A1: 因為flush 指令,是可以繞過read only并産生binlog的。
Q2:假如從庫start slave失敗,我也可以手動修複吧。
A2:
如果隻是切換一次,我相信你可以,如果切換5次,10次呢。
如果隻是今天早slave操作了,你姑且可以記住。如果是半年前的操作呢?你怎麼确定這個日志是可以skip的?
Q3:從庫的binlog怎麼會被purge呢?
A3:這個一般網際網路公司的binlog日志,線上不會保留太長時間,保留1個月已經算是謝天謝地了。 即便不是人為的purge,也會通過expire_logs來删掉的。
這個原理非常簡單,但是 越簡單的事情 卻 不容易做到。