導讀
MySQL主從複制環境中,如何才能保證主從資料的一緻性呢?
關于主從複制
現在常用的MySQL高可用方案,十有八九是基于 MySQL的主從複制(replication)來設計的,包括正常的一主一從、雙主模式,或者半同步複制(semi-sync replication)。
我們常常把MySQL replication說成是MySQL同步(sync),但事實上這個過程是異步(async)的。大概過程是這樣的:
- 在master上送出事務後,并且寫入binlog,傳回事務成功标記;
- 将binlog發送到slave,轉儲成relay log;
- 在slave上再将relay log讀取出來應用。
步驟1和步驟3之間是異步進行的,無需等待确認各自的狀态,是以說MySQL replication是異步的。
MySQL semi-sync replication在之前的基礎上做了加強完善,整個流程變成了下面這樣:
- 首先,master和至少一個slave都要啟用semi-sync replication模式;
- 某個slave連接配接到master時,會主動告知目前自己是否處于semi-sync模式;
- 在master上送出事務後,寫入binlog後,還需要通知至少一個slave收到該事務,等待寫入relay log并成功重新整理到磁盤後,向master發送“slave節點已完成該事務”确認通知;
- master收到上述通知後,才可以真正完成該事務送出,傳回事務成功标記;
- 在上述步驟中,當slave向master發送通知時間超過rpl_semi_sync_master_timeout設定值時,主從關系會從semi-sync模式自動調整成為傳統的異步複制模式。
半同步複制看起來很美好有木有呢,但如果網絡品質不高,是不是出現抖動,觸發上述第5條的情況,會從半同步複制降級為普通複制;此外,采用半同步複制,會導緻master上的tps性能下降非常嚴重,最嚴重的情況下可能會損失50%以上。
這樣來看,除非需要非常嚴格保證資料一緻性等迫不得已的場景,就不太建議使用半同步複制了。當然了,事實上我們也可以通過加強程式端的邏輯控制,來避免主從資料不一緻時發生邏輯錯誤,比如說如果在從上讀取到的資料和主不一緻的話,那麼就觸發主從間的一次資料修複工作。或者,我們也可以用 pt-table-checksum & pt-table-sync 兩個工具來校驗并修複資料,隻要運作頻率适當,是可行的。
真想要提高多節點間的資料一緻性,可以考慮采用PXC方案。現在已知用PXC規模較大的有qunar、sohu,如果團隊裡初期沒有人能比較專注PXC的話,還是要謹慎些,畢竟和傳統的主從複制差異很大,出現問題時需要花費更多精力去排查解決。
如何保證主從複制資料一緻性
上面說完了異步複制、半同步複制、PXC,我們回到主題:在正常的主從複制場景裡,如何能保證主從資料的一緻性,不要出現資料丢失等問題呢?
在MySQL中,一次事務送出後,需要寫undo、寫redo、寫binlog,寫資料檔案等等。在這個過程中,可能在某個步驟發生crash,就有可能導緻主從資料的不一緻。為了避免這種情況,我們需要調整主從上面相關選項配置,確定即便發生crash了,也不能發生主從複制的資料丢失。
1. 在master上修改配置
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
上述兩個選項的作用是:保證每次事務送出後,都能實時重新整理到磁盤中,尤其是確定每次事務對應的binlog都能及時重新整理到磁盤中,隻要有了binlog,InnoDB就有辦法做資料恢複,不至于導緻主從複制的資料丢失。
2. 在slave上修改配置
master_info_repository = "TABLE"
relay_log_info_repository = "TABLE"
relay_log_recovery = 1
上述前兩個選項的作用是:確定在slave上和複制相關的中繼資料表也采用InnoDB引擎,受到InnoDB事務安全的保護,而後一個選項的作用是開啟relay log自動修複機制,發生crash時,會自動判斷哪些relay log需要重新從master上抓取回來再次應用,以此避免部分資料丢失的可能性。
通過上面幾個選項的調整,就可以確定主從複制資料不會發生丢失了。但是,這并不能保證主從資料的絕對一緻性,因為,有可能設定了ignore\do\rewrite等replication規則,或者某些SQL本身存在不确定因素,或者人為在slave上修改資料,最終導緻主從資料不一緻。這種情況下,可以采用pt-table-checksum 和 pt-table-sync 工具來進行資料的校驗和修複。