天天看點

MySQL|複制 - 原生複制的一緻性探讨

MySQL高可用方案很多,最常見的原生複制方案,即async、semi-sync那套,是以本文從原生複制方案為中心,讨論資料一緻性。

基于複制的高可用方案

  • 兩節點:雙主、主從
  • 三節點或更多:一主兩從等

明确一點,這幾種結構無論怎麼變,無論是用MHA還是Orchestrator,還是什麼第三方工具,核心點都是通過

binlog event

來傳遞資料給其他節點。

這種結構常見的問題:

1、延遲、複制過濾、報錯等造成資料不一緻:

這個問題,一般能夠通過優秀的DBA和業務開發大哥們,通過權限管理、schema設計、配置優化、SQL調優等手段處理掉,但……還有2

2、master crash,備庫沒有收到binlog event:

這個問題,有人就會說了,有個叫

semi-sync

的東西,何況5.7還優化了

semi-sync

,支援無損半同步(

lossless semi-synchronous

),但其實極端情況下,也有問題。

MySQL|複制 - 原生複制的一緻性探讨

異步複制

大家都懂。聽名字和實際上都不靠譜:不管一個或多個從庫是否收到了

binlog events

,主庫隻管産生

binlog

,極端場景下的,發生資料不一緻的幾率非常大。

半同步複制

先簡單說一下

after_commit

after_sync(lossless)

的差異。

after_commit:
    在engine commit之後等待ACK
after_sync:
    在engine commit之前等待ACK    
           

複制

after_commit

模式下,先做引擎層送出,這時候其他用戶端是可以會讀到這個事務的。如果這時發生容災,事務又沒有傳給

Slave

,容災後用戶端就讀不到這個事務了,就出現了資料不一緻情況。

MySQL|複制 - 原生複制的一緻性探讨
MySQL|複制 - 原生複制的一緻性探讨

(出處見參考文檔)

after_sync(lossless)

優化了這點,即,等待

ACK

傳回成功,再做引擎層送出,這樣用戶端1讀不到,其他用戶端也讀不到。

MySQL|複制 - 原生複制的一緻性探讨

(出處見參考文檔)

MySQL|複制 - 原生複制的一緻性探讨

(圖源宋利兵老師,來自知數堂的公開課-《MySQL-5.7 Replication新特性》)

基于半同步複制的容災讨論

讨論這個話題其實是非常大的,因為複制的場景很多。比如GTID、auto-position、MTS等。

此處假設大家的MySQL執行個體都是經過專業DBA之手,配置好了支援

slave crash safe

的參數。

此處讨論5.7+的

after_sync(lossless)

Master Crash,未容災:

沒所謂,拉起後繼續做crash recovery,最終主從是一緻的。

Master Crash,容災:

1)Crash時至少有一台Slave收到binlog event并成功傳回了ACK給Master:

沒問題,因為這種情況主從資料一緻的,拉起後也不需要做額外操作。

2)Crash時binlog event發送失敗:

假設産生在執行個體A的事務t1是:

INSERT:pk_id=11, name='張三'

發生容災,執行個體B成為新主,可能會産生新事務t2:

INSERT:pk_id=11, name='李四'

執行個體A恢複後,做

crash recovery

,将t1直接送出,然後作為新的Slave,接收到了執行個體B(新主)的這條事務t2,出現了

duplicated key

,經典的

ERROR 1062

來了,設定合理的參數也沒用。

其實,事務t1是要被遺棄的小可憐(鵝廠PhxSQL團隊稱這種事務為“Pending Binlog”)

因為也沒有傳回給用戶端它成功送出的資訊,這種情況是要復原的,而且這種情況在

after_sync(lossless)

的設定下,也沒辦法的。

3)Crash時binlog event發送成功,但在等ACK,沒有執行

engine commit

因為事務t1已經發送成功了,接管成新Master的Slave是有事務t1的,是以原Master拉起後,做

crash recovery

,也是沒問題的,拉起後不需要做額外操作。

場景2)還有什麼風險?

試想一下,如果沒有pk,是不是就不會報錯?

也沒辦法發現拉起來的老Master實際上和新Master資料不一緻了?

場景2)的解決:

首先,場景2)MySQL原生複制結構是沒法處理的。

但問題總要解決,為了保證整個複制節點的資料一緻,考慮如下:

方案1、重建節點
方案2、復原錯誤事務,斷點續傳新增binlog
           

複制

方案1也就是最憨的辦法,直接重建容災後的從庫,沒有技術含量,有個叫做

xtrabackup

的家夥,拿它一把梭,不說了。

方案2需要一定的開發量,參(bai)考(piao)了一下别人的做法:

微信PhxSQL:

額外多一個BinlogSvr的中間服務。“使得Master(重新開機時檢查本地Binlog是否和BinlogSvr叢集的資料一緻)和Slave(從BinlogSvr叢集中擷取Binlog)的資料保持一緻,進而保證了整個叢集中的MySQL主備間資料的一緻性。”

MySQL|複制 - 原生複制的一緻性探讨

如果這個時候有人問,“你把

pk_id=11, name='張三'

給復原了,這個事務不是丢掉了嗎?”

我認為復原是沒問題的。

理由如下:

事務的ACID特性,其中的D持久性是指“送出成功的事務肯定不丢”。而我上面所說的場景是——沒有傳回“送出成功”,是以我認為復原是沒問題的。

結論

原生複制存在對“一緻性”和“可用性”的權衡挑戰。

即使是安全的配置了各種參數,使用原生複制體系和

lossless semi-sync

,也不是100%省心的(尤其是對于資料一緻性要求拉滿的業務)。

即使是将

@@rpl_semi_sync_master_timeout

設定為無窮大,也隻是保證

semi-sync

不退化。

其他一緻性方案或思路:

1、Paxos\Raft
2、共享存儲           

複制

參考

  • 《PhxSQL設計與實作》,google一搜就有
  • 《Loss-less Semi-Synchronous Replication on MySQL 5.7.2》,http://my-replication-life.blogspot.com/2013/09/loss-less-semi-synchronous-replication.html
  • 淘寶核心月報 - 《MySQL 半同步複制資料一緻性分析》,http://mysql.taobao.org/monthly/2017/04/01/