故障案例:高可用切換後數據不一致,舊主庫數據丟失

故障現象:

有臺物理機宕機,複製架構是master-master+半同步,期間觸發了高可用容災切換,後臺顯示成功切換到了備庫。但是等舊主庫起來後,主從狀態正常,但是舊主庫上卻丟失了一部分數據,通過對比發現這些數據都是在宕機瞬間的寫操作

原因分析:

1 假設舊主庫A丟失的數據記錄爲X,我們去A上解析發現宕機那段時間並無X記錄的binlog信息,可確定在宕機瞬間該記錄X沒有binlog落盤;
2 因爲我們開了log_slave_updates,我們去新主庫B上解析那段時間的binlog,發現存在binlog記錄,並且server-id是A的,可確定這個X記錄是舊主庫A傳給新主庫B的,而不是B自己產生的
3 初步猜測,宕機瞬間舊主庫A部分binlog沒有落盤,但是卻已經傳到了新主庫B,等舊主庫通過redo異常恢復時,因爲binlog中無X記錄的操作,則直接回滾了該操作,導致A上無X記錄,而X記錄已經存在備庫B的binlog中併成功執行,導致雙主兩邊數據不一致
3 那麼主要需要排查的就是:爲什麼主庫binlog沒有落盤的情況下,能否直接傳給從庫?
4 通過查看參數設置,發現A上將sync_binlog設置爲0,即binlog落盤時間完全由操作系統決定,而不是每次操作都會將binlog落盤,這樣舊主庫A在宕機時就可能丟失部分binlog;
MYSQL_BIN_LOG::sync_binlog_file(bool force)
{
  bool synced= false;
  unsigned int sync_period= get_sync_period();//獲取sync_binlog參數值
  if (force || (sync_period && ++sync_counter >= sync_period))
  {
    sync_counter= 0;
binlog寫文件的情況
1 強制寫
2 sync_binlog>=1且binlog寫入次數>=sync_binlog數值
3 操作系統決定


而從庫去主庫讀取binlog時,並不一定要等到binlog落盤,而是直接從pagecache中讀取;這也就可能產生這個現象:
線程1寫文件,還沒fsync;
線程2讀文件,直接從pagecache中讀取成功;
主庫宕機,fsync失敗


通過查看源碼發現,代碼已經給出瞭解釋:sync_binlog爲1的情況下可保證在主庫binlog落盤前,事件不會先被dump線程取走
need_LOCK_log= (get_sync_period() == 1);


  /*
    LOCK_log is not released when sync_binlog is 1. It guarantees that the
    events are not be replicated by dump threads before they are synced to disk.
  */
  if (change_stage(thd, Stage_manager::SYNC_STAGE, wait_queue,
                   need_LOCK_log ? NULL : &LOCK_log, &LOCK_sync))
  {
    DBUG_PRINT("return", ("Thread ID: %lu, commit_error: %d",
                          thd->thread_id, thd->commit_error));
    DBUG_RETURN(finish_commit(thd));
  }
  final_queue= stage_manager.fetch_queue_for(Stage_manager::SYNC_STAGE);
線程讀取和寫文件的方式並沒有改變,所以mysql在內部使用了鎖來控制時序性,在binlog落盤前加鎖,阻塞線程2讀取文件,直到線程1 fsync完成後才釋放,這樣在sync_binlog=1的情況下就可以簡單理解成串行先執行binlog落盤完成後再被dump線程讀取了。


解決方法

1 人工修復這部分數據,數量多的話重做主從
2 將sync_binlog改爲1,杜絕這個情況再次發生
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章