掃二維碼與項目經理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯網交流
作者:陳俊超 2017-04-17 09:54:34
大數據
分布式 本文詳細描述了PhxSQL的設計與實現。從MySQL的容災缺陷開始講起,接著闡述實現高可用強一致的思路,然后具體分析每個實現環(huán)節(jié)要注意的要點和解決方案,最后展示了PhxSQL在容災和性能上的成果。

我們提供的服務有:成都網站設計、網站建設、外貿網站建設、微信公眾號開發(fā)、網站優(yōu)化、網站認證、長清ssl等。為上千余家企事業(yè)單位解決了網站和推廣的問題。提供周到的售前咨詢和貼心的售后服務,是有科學管理、有技術的長清網站制作公司
本文詳細描述了PhxSQL的設計與實現。從MySQL的容災缺陷開始講起,接著闡述實現高可用強一致的思路,然后具體分析每個實現環(huán)節(jié)要注意的要點和解決方案,***展示了PhxSQL在容災和性能上的成果。
設計背景
互聯網應用中賬號和金融類關鍵系統(tǒng)要求和強調強一致性及高可用性。當面臨機器損壞、網絡分區(qū)、主備手工或者自動切換時,傳統(tǒng)的MySQL主備難以保證強一致性和高可用性。PhxSQL將MySQL集群構建在一致性完善的Paxos協議基礎上,保證了集群內MySQL機器之間數據的強一致性和整個集群的高可用性。
原生MySQL的容災缺陷 【MySQL容災方案】
MySQL有兩種常見的復制方案,異步復制和半同步復制。
1、異步復制方案
Master對數據進行commit操作后再將數據異步復制到Slave。
但數據無法保證成功復制,也就無法保證MySQL主備間的數據一致性,如圖1所示。
圖1 MySQL異步復制流程
2、半同步復制方案
Master對數據進行commit操作前將數據復制到Slave,確認復制成功后再對數據進行commit操作。
絕大多數情況下,半同步復制能保證MySQL主備間的數據一致性,如圖2所示。
圖2 MySQL半同步復制流程
【MySQL重啟流程】
半同步方案中的“半”是指Master在等待Slave的ACK失敗時將退化成異步復制。同時,MySQL在重啟時也不會執(zhí)行半同步復制。
如圖3中的id(Gtid)=101數據是Master機器中新寫入到Binlog File的Binlog數據。但Master在復制數據到Slave的過程中MySQL宕機導致復制失敗。MySQL重啟時,數據(id=101)會被直接進行commit操作,隨后再將數據異步復制到Slave。(下文將已經寫入到Binlog File但未進行commit操作的數據(id=101)稱為Pending Binlog。)
圖3 MySQL重啟時直接提交Pending Binlog
該情況下MySQL容易出現Master-Slave之間數據不一致的情況,官方也描述了該問題。
http://bugs.mysql.com/bug.php?id=80395
https://mariadb.atlassian.net/browse/MDEV-162
【MySQL重啟缺陷】
下面將解釋MySQL在重啟時不執(zhí)行半同步會產生數據不一致的原因。
當對上述例子中的Pending Binlog(id=101)進行復制時Master宕機導致復制失敗,隨后Slave1切換成新Master并開始提供服務(寫入id=201的數據)。此后,當舊Master重啟時,Pending Binlog(id=101)不會被重新進行復制而直接進行commit操作,從而導致舊Master比新Master多了一條數據,舊Master無法成為新Master的Slave,需要人工處理掉這條數據之后,才能讓舊Master作為Slave提供服務,如圖4所示。
圖4 MySQL重啟缺陷導致主備數據不一致
上述case只對舊Master的數據造成影響,不會使得MySQL Client讀取到錯誤數據。但當Master連續(xù)出現兩次宕機后產生Master切換,兩次宕機間隔較短使得Pending Binlog未能及時復制到Slave,且期間有查詢請求時(Master宕機→Master重啟→查詢數據→Master宕機→Master切換),MySQL Client會產生如圖5所示的幻讀(兩次讀到的結果不一致)。
圖5 MySQL重啟缺陷導致Client產生幻讀
【MySQL Client分裂】
當Master出現故障且產生Master切換時,由于原生MySQL缺乏調用端的通知/重定向機制,使得不同的Client可能訪問不同的Master,導致數據的錯誤寫入和讀取,如圖6所示。
圖6 MySQL進行Master導致Client端分裂
【MySQL缺乏自動選主機制】
由于半同步復制不需要等待所有Slave的ACK,因此當Master出現故障時,需要選有***Binlog的Slave為新的Master;而MySQL并沒有內置這個選主機制,如圖7所示。
圖7 MySQL缺少自動選主機制
【MySQL的容災缺陷總結】
因此MySQL在容災上無法同時滿足數據強一致和服務高可用兩個特性。
PhxSQL設計思路
【可靠日志存儲】
實現一個以可靠日志存儲為中心的架構來解決MySQL數據復制時產生的數據不一致問題。
Master將Binlog發(fā)送到BinlogSvr集群(可靠日志存儲),Slave從BinlogSvr集群獲取Binlog數據完成數據復制。
Master在重啟時,根據BinlogSvr集群的數據判斷Pending Binlog是否已經被復制。如果未被復制則從Binlog File中刪除。
利用BinlogSvr集群(可靠日志存儲),使得Master(重啟時檢查本地Binlog是否和BinlogSvr集群的數據一致)和Slave(從BinlogSvr集群中獲取Binlog)的數據保持一致,從而保證了整個集群中的MySQL主備間數據的一致性,如圖8所示。
圖8 實現一個可靠日志存儲保證各MySQL的數據一致
【請求透傳】
在Master進行切換時,切換操作可能會導致部分MySQL Client仍然訪問舊Master并讀到舊數據。
最直觀的方法是修改MySQL Client API,在每一次進行查詢時,先確認當前Master的位置。但此方法有以下缺點:
為了避免修改MySQL Client API,可通過增加Proxy進行請求透傳來解決上述問題。在每一個MySQL結點上增加一個Proxy,MySQL Client的請求不再直接訪問MySQL而直接訪問Proxy。Proxy根據Master的位置,將訪問Slave機器的請求透傳到Master機器,再進行MySQL操作。
通過增加Proxy進行請求透傳,解決了MySQL Client分裂導致有可能讀取到舊數據的問題,如圖9所示。
圖9 實現一個可靠日志存儲保證各MySQL的數據一致
【自動選主】
多機自動選主最常見的實現方式是由各個參與者發(fā)起投票,獲得多數派支持的機器為Master,同時把Master信息記錄到可靠存儲。Master機器定期到可靠存儲延長租約;非Master機器定期檢查Master租約是否過期,從而決定是否要發(fā)起選舉自己為Master的投票。
為了避免修改MySQL代碼,在MySQL機器上增加一個Agent,由Agent來替代MySQL發(fā)起選主投票和續(xù)期租約;可靠存儲繼續(xù)由BinlogSvr承擔。
Agent完成以下功能:
圖10 可靠日志存儲和Agent共同實現自動選主機制
PhxSQL架構和實現
從上述思路可以得出PhxSQL的簡單三層架構。對于每一個節(jié)點,部署3個模塊(PhxSQLProxy,MySQL,PhxBinlogSvr)。多個節(jié)點上的PhxBinlogSvr組成一個可靠的日志存儲集群和可靠的Master信息存儲集群;PhxBinlogSvr同時承擔Agent的責任。PhxSQLProxy負責請求的透傳。Master結點上的PhxSync負責將MySQL的Binlog發(fā)送到PhxBinlogSvr,如圖11所示。
圖11 PhxSQL基本架構
【Proxy(PhxSQLProxy)】
請求透傳
請求透傳是Proxy主要的功能。主要解決在進行Master切換的時候,MySQL Client會被分裂,不同的Client可能連接到不同的MySQL。導致出現MySQL Client寫入數據到錯誤的Master或者從錯誤的Master讀取到錯誤的數據。
Proxy的請求透傳分兩種:
圖12 Proxy請求透傳流程
高性能:由于Proxy接管了MySQL Client的請求,為了使整個集群的讀寫性能接近單機MySQL,Proxy使用協程模型提高自身的處理能力。
Proxy的協程模型使用開源的Libco庫。Libco庫是微信團隊開源的一個高性能協程庫,具有以下特點:
完全兼容MySQL:為了已有的應用程序能夠不做任何修改就能遷移到PhxSQL,Proxy需兼容MySQL的所有功能。
兼容MySQL事務
MySQL事務管理基于連接,同一個事務的所有請求通過同一個連接通信。在事務處理中連接丟失,事務將被rollback( http://dev.mysql.com/doc/refman/5.6/en/innodb-autocommit-commit-rollback.html )。
Proxy使用1:1連接模型完全兼容MySQL事務。每當MySQL Client發(fā)起一個連接到Proxy,Proxy都會相應地發(fā)起一個連接到MySQL。兩條連接中,任意一個中斷,另外一個也相應斷開,對應的事務會被rollback,如圖13所示。
圖13 Proxy的1對1事務連接模型
兼容MySQL權限
MySQL的權限管理基于(用戶,源IP)對,源IP是通過socket句柄反查獲取。當請求通過Proxy連接到MySQL時,源IP為Proxy本地IP,權限管理會出現異常。
Proxy利用MySQL協議HEAD保留字段透傳真實源IP到MySQ,MySQL再從HEAD保留字段獲取正確的源IP進行權限管理,如圖14所示。
圖14 Proxy通過修改MySQL協議兼容MySQL權限
PhxSync
PhxSync的功能和MySQL的semisync插件類似。經過調研,對semisync插件的接口做少量的調整,就可以使用這些插件接口來實現PhxSync。
PhxSync功能主要是:
由于MySQL沒有提供在重啟時的插件接口,為了后續(xù)維護方便,在MySQL代碼層抽象出了一個新插件接口before_binlog_init用于校準Binlog。
上述對after_flush接口的調整,和新增的before_binlog_init接口已經提交補丁給MySQL官方( http://bugs.mysql.com/bug.php?id=83158 )。
【PhxBinlogSvr】
PhxBinlogSvr主要負責存儲Binlog和Master信息的維護。在數據復制階段,通過Paxos協議保證PhxBinlogSvr各節(jié)點的數據一致性(下文稱PhxBinlogSvr為BinlogSvr)。
BinlogSvr異常情況處理 防止Slave的節(jié)點提交數據
當舊Master在提交數據時由于網絡問題數據包被卡在網絡,且新Mater已經成功切換時,或者人為錯誤直接往Slave節(jié)點的MySQL寫入數據時,則會出現Slave節(jié)點提交數據的情況。多節(jié)點同時提交數據會出現BinlogSvr的Binlog數據和MySQL存儲的Binlog數據不一致的情況。
BinlogSvr存儲了集群內的Master信息。當其收到MySQL提交的數據時,可根據Master信息拒絕非Master節(jié)點的提交,如圖15所示。
圖15 BinlogSvr通過Master信息拒絕非Master節(jié)點的提交
防止Master提交錯誤數據
在某些情況下,Master可能會重新發(fā)送數據或者發(fā)送錯誤數據。譬如在網絡不好的情況下Master由于提交數據超時而重發(fā)數據。磁盤發(fā)生故障或者數據被錯誤回滾或者修改的時候,Master會提交錯誤的數據。
BinlogSvr使用樂觀鎖機制來防止Master的異常提交。在MySQL提交數據給BinlogSvr時,以本機MySQL已經執(zhí)行的GTID為樂觀鎖,提交的內容為(本機MySQL已經執(zhí)行的***GTID,本次要提交的Binlog)。BinlogSvr通過檢查請求中(本機MySQL已經執(zhí)行的***GTID)和自身保存的***GTID是否匹配來拒絕重新發(fā)送或者異常發(fā)送的數據,如圖16所示。
圖16 BinlogSvr使用樂觀鎖拒絕Master在數據異常的情況下提交數據
圖17 BinlogSvr支持MySQL使用原生復制協議獲取Binlog數據
BinlogSvr通過Paxos協議進行Master選舉,選舉成功后成為Master并擁有租約。通過Paxos協議選舉保證了最終只產生一個Master且每個節(jié)點記錄了一致的Master信息。
PhxSQL效果 【PhxSQL數據一致性】
通過比較PhxSQL集群中各節(jié)點的數據(MySQL Binlog,PhxPaxos,BinlogSvr) 判斷各節(jié)點數據是否一致,如圖18所示。
圖18 PhxSQL 3機數據對比
【Master自動切換】
通過觀察Master宕機時各節(jié)點的流量變化判斷Master是否順利切換。下圖中的紅線代表流量。當Master宕機時,流量會隨之轉移,代表Master順利切換,如圖19所示。
圖19 PhxSQL進行Master切換時各節(jié)點的寫入流量變化
【PhxSQL性能】
MySQL版本:Percona 5.6.31-77.0
機器信息:
CPU :Intel Xeon CPU E5-2420 0 @ 1.90GHz * 24。
Memory : 32G。
Disk:SSD Raid10。
Ping Costs:Master→Slave:3 ~ 4ms; client→Master :4ms。
工具和參數:
- sysbench。
- –oltp-tables-count=10 –oltp-table-size=1000000 –num-threads=500。
- –max-requests=100000 –report-interval=1 –max-time=200。
PhxSQL的寫性能比MySQL的半同步好,讀性能由于多了一層Proxy導致比MySQL的半同步稍差。
圖20 PhxSQL和MySQL的性能對比

我們在微信上24小時期待你的聲音
解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯網交流