1. 程式人生 > >mysql資料庫之主從不一致場景分析及如何避免

mysql資料庫之主從不一致場景分析及如何避免

master庫寫redo、binlog不實時丟資料的場景

redo的ib_logfile與binlog日誌如果被設定非實時flush,就有可能存在丟資料的情況:


redo未寫入磁碟,但binlog寫入磁碟,造成從庫資料量比主庫多。
redo寫入了磁碟,但是binlog未寫入,造成從庫資料量比主庫少。


從目前來看,只能犧牲效能去換取資料的安全性,必須要設定redo和binlog為實時刷盤,如果對效能要求很高,則考慮使用SSD:

 

sync_binlog = 1
innodb_flush_log_at_trx_commit = 112

 

slave庫寫redo、binlog不實時丟資料的場景

slave讀取master的binlog日誌後,需要落地3個檔案:relay log、relay log info、master info: 
        relay log:即讀取過來的master的binlog,內容與格式與master的binlog一致。 
        relay log info:記錄SQL Thread應用的relay log的位置、檔案號等資訊。 
        master info:記錄IO Thread讀取master的binlog的位置、檔案號、延遲等資訊。 
    因此如果當這3個檔案如果不及時落地,則主機crash後會導致資料的不一致。

MySQL 5.6 針對複製功能提供了新特性: slave支援crash-safe. 該功能可以解決之前版本中系統異常斷電可能導致的SQL thread 資訊不準確的問題。 
在MySQL 5.6.2之前,slave記錄的master資訊以及slave應用binlog的資訊存放在檔案中,即master.info與relay-log.info。在5.6.2版本之後,允許記錄到table中,引數設定如下:

 

master-info-repository  = TABLE 
relay-log-info-repository = TABLE 12

對應的表分別為mysql.slave_master_info與mysql.slave_relay_log_info,且這兩個表均為innodb引擎表。

master info與relay info還有3個引數控制重新整理: 
  ● sync_relay_log:這個引數和sync_binlog是一樣的,當設定為1時,slave的I/O執行緒每次接收到master傳送過來的binlog日誌都要寫入系統緩衝區,然後刷入relay log中繼日誌裡,這樣是最安全的,因為在崩潰的時候,你最多會丟失一個事務,但會造成磁碟的大量I/O。當設定為0時,並不是馬上就刷入中繼日誌裡,而是由作業系統決定何時來寫入,雖然安全性降低了,但減少了大量的磁碟I/O操作。這個值預設是0,可動態修改,建議採用預設值。預設為10000,即每10000次sync_relay_log事件會重新整理到磁碟。為0則表示不重新整理,交由OS的cache控制。 
  ● sync_master_info:若master-info-repository為FILE,當設定為0,則每次sync_master_info事件都會重新整理到磁碟,預設為10000次重新整理到磁碟;若master-info-repository為TABLE,當設定為0,則表不做任何更新,設定為1,則每次事件會更新表 #預設為10000 
  ● sync_relay_log_info:若relay_log_info_repository為FILE,當設定為0,交由OS重新整理磁碟,預設為10000次重新整理到磁碟;若relay_log_info_repository為TABLE,且為INNODB儲存,則無論為任何值,則都每次evnet都會更新表。

建議引數設定如下:

 

sync_relay_log = 1 
sync_master_info = 1 
sync_relay_log_info = 1 
master-info-repository  = TABLE 
relay-log-info-repository = TABLE 12345

當這樣設定,導致呼叫fsync()/fdatasync()隨著master的事務的增加而增加,且若slave的binlog和redo也實時重新整理的話,會帶來很嚴重的IO效能瓶頸。

同時,通過MySQL 5.5版本開始提供的引數relay_log_recovery,當slave發生crash後重啟之後重連master時,slave不根據master-info.log的資訊進行重連,而是根據relay-info中執行到master的位置資訊重新開始拉master上的日誌資料。

總結:crash-safe + relay_log_recovery

 

複製有延遲的情況下主庫宕機造成的資料丟失

當master出現故障後,binlog未及時傳到slave,或者各個slave收到的binlog不一致。且master無法在第一時間恢復,這個時候怎麼辦? 
   如果master不切換,則整個資料庫只能只讀,影響應用的執行。 
   如果將別的slave提升為新的master,那麼原master未來得及傳到slave的binlog的資料則會丟失,並且還涉及到下面2個問題。 
      1.各個slave之間接收到的binlog不一致,如果強制拉起一個slave,則slave之間資料會不一致。 
      2.原master恢復正常後,由於新的master日誌丟棄了部分原master的binlog日誌,這些多出來的binlog日誌怎麼處理,重新搭建環境?

對於上面出現的問題,一種方法是確保binlog傳到從庫,或者說保證主庫的binlog有多個拷貝。第二種方法就是允許資料丟失,制定一定的策略,保證最小化丟失資料。

如何確保binlog全部傳到從庫? 
   方案一:使用semi sync(半同步)或增強型半同步方式,事務提交後,必須要傳到slave,事務才能算結束。對效能影響很大,依賴網路適合小tps系統。當半同步複製發生超時時(由rpl_semi_sync_master_timeout引數控制,單位是毫秒,預設為10000,即10s),會暫時關閉半同步複製,轉而使用非同步複製。當master dump執行緒傳送完一個事務的所有事件之後,如果在rpl_semi_sync_master_timeout內,收到了從庫的響應,則主從又重新恢復為半同步複製。由引數rpl_semi_sync_master_wait_point控制,取值為AFTER_SYNC (預設值)何AFTER_COMMIT 
   方案二:雙寫binlog,通過DBDR OS層的檔案系統複製到備機,或者使用共享盤儲存binlog日誌。 
   方案三:在資料層做文章,比如保證資料庫寫成功後,再非同步佇列的方式寫一份,部分業務可以藉助設計和資料流解決。

參考: 
MySQL丟資料及主從資料不一致的場景 
https://www.2cto.com/database/201702/593470.html