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
),但其實極端情況下,也有問題。

異步複制
大家都懂。聽名字和實際上都不靠譜:不管一個或多個從庫是否收到了
binlog events
,主庫隻管産生
binlog
,極端場景下的,發生資料不一緻的幾率非常大。
半同步複制
先簡單說一下
after_commit
和
after_sync(lossless)
的差異。
after_commit:
在engine commit之後等待ACK
after_sync:
在engine commit之前等待ACK
複制
after_commit
模式下,先做引擎層送出,這時候其他用戶端是可以會讀到這個事務的。如果這時發生容災,事務又沒有傳給
Slave
,容災後用戶端就讀不到這個事務了,就出現了資料不一緻情況。
(出處見參考文檔)
而
after_sync(lossless)
優化了這點,即,等待
ACK
傳回成功,再做引擎層送出,這樣用戶端1讀不到,其他用戶端也讀不到。
(出處見參考文檔)
(圖源宋利兵老師,來自知數堂的公開課-《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主備間資料的一緻性。”
如果這個時候有人問,“你把
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/