微信開源PhxSQL背后:強一致高可用分布式數據庫的設計和實現哲學
微信技術團隊最近在開源開放上動作不斷,后端方面前段時間他們開源了生產級paxos類庫PhxPaxos,現在又開源了高可用分布式數據庫PhxSQL,本文我們將了解一些PhxSQL背后的故事,以及它設計和實現的哲學。
老司機簡介
陳明 , PhxSQL負責人。2012年加入微信,先后負責微信消息、朋友圈、賬號、關系鏈、全球分布式部署、PhxSQL等項目,見證了微信后臺基礎架構支持從一億用戶到八億用戶、從單點到全球分布、從粗放到追趕和超越的歷史過程。在加入微信之前,陳明在搜搜從事搜索架構建設和在微軟亞洲研究院研究分布式架構及存儲。陳明從清華大學計算機系獲得博士學位,研究方向是分布式系統。
PhxSQL開源地址見:
https://github.com/tencent-wechat/phxsql
為何研發PhxSQL?
PhxSQL是一個兼容MySQL、服務高可用、數據強一致的關系型數據庫集群。PhxSQL以單Master多Slave方式部署,在集群內超過一半機器存活的情況下、即可提供服務,并且自身實現自動Master切換、保證數據一致性。PhxSQL不依賴于Zookeeper等任何第三方做存活檢測及選主。PhxSQL基于MySQL的一個分支Percona 5.6開發,功能和實現與MySQL基本一致。
PhxSQL可以提供和zookeeper相同的強一致性與高可用性,并且支持serializable級別的事務隔離。
陳明表示,微信研發PhxSQL是為了解決在應用MySQL時一個普遍且巨大的痛點:強一致和高可用,MySQL不支持PhxSQL面向的分布式環境下強一致和高可用應用場景,而這在金融和賬號類關鍵應用是必須的。據他們所知,目前還未有類似的公開方案,因此,只能自己研發。
后面的正文里面,也會詳細的討論MySQL在分布式環境下的痛點。
PhxSQL目前應用在微信后臺的賬號系統、企業微信、及QQ郵箱。其中一個關鍵服務的集群由5臺服務器構成一個組,容忍兩機同時故障,寫高峰2000/秒,讀高峰60000/秒。
性能與架構遷移
在他們的基準測試中,PhxSQL相對于MySQL的寫性能有16-25%的提升,讀性能持平。具體測試報告參見 github/phxsql 網址。實際上,PhxSQL的主要目標是強一致、高可用、和serializable事務隔離;性能只是一個關注點。另外,PhxSQL將很快支持性能更好的5.7版本MySQL。
PhxSQL設計的一個基本原則就是完全兼容MySQL,MySQL客戶端不需要任何修改就可以直接訪問PhxSQL,以便于將現有的。但是由于MySQL客戶端不支持服務器故障時連接到新的可用節點,他們提供了多種可選方案。
- PhxSQLProxy可以將客戶端請求轉發給可用節點。
- PhxSQL client lib支持自動切換服務器,原有應用程序只要在源碼中創建連接時傳入PhxSQL服務器IP列表、鏈接時將MySQL client lib換成PhxSQL client lib即可。
- VIP等方案也可以和PhxSQL配合使用。
有些人可能會注意到PhxPaxos和PhxSQL都帶有前綴Phx,陳明說Phx代表不死的鳳凰,是鳳凰英文單詞phoenix的縮寫。它將用于微信后端的一系列開源項目,后續他們將放出更多的開源項目,敬請期待。
PhxSQL的設計和實現哲學
PhxSQL發布以來,受到很多關注。作為熱愛技術的碼農,我們感謝大家的關心和支持,歡迎一切基于技術出發點的討論。“Show you the code”之后,我們在這里談談PhxSQL的設計和實現哲學,也同時回答大家提出的一些疑問。
1、 PhxSQL是什么?
PhxSQL是一個通過Paxos保證強一致和高可用的的MySQL集群。 PhxSQL建立在Paxos的一致性和MySQL的binlog流水基礎上。主要原理簡單來說:
- Paxos選出主機
- 主機把本機MySQL設置成可寫的MySQL主機,在MySQL寫binlog流程中攔截binlog流水、發送到Paxos,形成全局的binlog流水
- 備機把本機的MySQL設置成只讀的MySQL備機,MySQL備機從全局binlog中拉取流水,重放和執行,從而主備MySQL一致
- 針對常見的業務場景,PhxSQL提供兩個服務端口:強一致讀寫端口(ReadWritePort)和只讀端口(ReadonlyPort);對數據要求強一致的業務,通過ReadWritePort來讀寫;只要求能讀取但不要求最新數據的讀請求(比如一些定時對賬業務),可以通過ReadonlyPort來讀取
只要有多于一半機器工作和互聯,PhxSQL就可以正常工作。
圖 1:PhxSQL架構
2、 “強一致”和“高可用”是什么級別?
很多MySQL集群方案都宣稱強一致和高可用。PhxSQL這方面有什么不同?
大家熟知的Zookeeper提供強一致和高可用。一致性有很多級別,從強到弱分別是:Strict(嚴格一致性),Linearizable(線性一致性),Sequential(序列一致性),Causal(因果一致性),Eventual等。
嚴格一致性只是一個理論模型。根據相對論,由于信息傳播的速度不可能高于光速,嚴格一致性在實際中幾乎無法實現。線性一致性的理論定義很復雜,不太嚴謹直觀地講,就是任何一個客戶端都可以讀到別的客戶端寫入的最新內容。這也是大家通常理解的強一致。
Zookeeper的強一致指“線性一致性”。高可用是說只要多于一半機器工作和互聯即可在保證線性一致性的質量下正常工作。PhxSQL的強一致是指“線性一致性”,高可用是指只要多于一半機器工作和互聯即可在保證線性一致性質量下工作。即,
PhxSQL提供和Zookeeper相同的強一致性和高可用性!
PhxSQL提供和Zookeeper相同的強一致性和高可用性!
PhxSQL提供和Zookeeper相同的強一致性和高可用性!
重要事情說三遍:)。大家可以把PhxSQL當Zookeeper使用,例如用來選主!
在數據庫事務隔離方面,PhxSQL支持最高級別的serializable。在性能方面,PhxSQL提供明顯優于MySQL半同步的寫性能和幾乎相同的讀性能[22]。
細心的讀者可能注意到,和通常的“高可用、強一致”說法順序相反,這個小節的標題中,“強一致”有意放在了“高可用”的前面。在這里需要澄清一個誤區。當談到高可用時,有時會有意無意忽略或者降低一致性。
嚴格意義上來講,可用性和一致性必須一塊討論,滿足一致性要求前提下的可用性才有意義。打個不嚴謹的比方,一致性就像汽車的“安全性”,可用性就是汽車的“可用性”。如果一輛車號稱“可用性”很好,可以連續工作10年,但從來不提“安全性”,那么這輛車的品質是值得懷疑的。
3、 為什么要做PhxSQL?
雖然有很多NoSQL、NewSQL系統、以及多主多寫MySQL集群、支持分庫分表的MySQL集群,MySQL傳統主備同步方案仍然具備很多新系統難以企及的優點。MySQL主備在主機上支持完整SQL、全局事務、以repeatable read和serializable級別的事務隔離,在金融、帳號等關鍵業務中有巨大的價值。同時,現有復雜MySQL應用遷移到新的非兼容系統成本也很高。
但是MySQL傳統主備方案也有其缺點。最明顯的就是主機故障后的自動換主和新舊主數據一致性(以及衍生的換主后主備一致性、各個備機之間一致性,這里就不詳加討論了),即所謂的一致性和可用性。
為了解決這個問題,有傳統流派:用Zookeeper、etcd、或者其它第三方來檢測心跳、選主、和切換。改造MySQL client讓其能感知新的主機。或者為了能讓傳統MySQL client不加修改就能感知新的主機,部署MySQL代理服務器,讓其將連到自身的MySQL client請求透明轉發給新的主機。
為了減少主備間數據的落后,從而降低舊主機故障、某臺備機被提升成新主機時,新舊主機之間、新主機和其它備機之間的差異,很多方案在主備同步機制上做了很多有益的工作。
例如semi-sync等待多數派備機應答,通過優化線程和網絡、主備多通道、備機并行執行binlog流水等盡量減少主備之間差異。如果主備間任何時刻都完全一致,那么任何時刻換主都是強一致的。這句話的另外一個意思是,如果無法保證主備間任何時刻完全一致,那么當有持續不斷的更新時,任何時刻的換主都是無法保證強一致的。
傳統流派另外一個分支就是將MySQL本地磁盤換成更高可靠性的SAN,當MySQL主機故障時,將SAN掛接到備機上提供服務。而當SAN故障時怎么辦?當需要跨機房部署時怎么辦?
意識到傳統流派的局限性,新出了Galera和MySQL Group Replication等。除了宣稱提供強一致性和高可用性外,還支持master-master多點寫入等誘人新特性。
世界上目前已知的經過理論證明和實際檢驗的一致性算法屈指可數:兩階段提交two-phase commit (2PC)、Paxos、Raft、Zookeeper Atomic Broadcast (ZAB)、Viewstamped Replication等。2PC雖然可以保證一致性,但在主機故障時無法工作,存在可用性問題。
目前被工業界廣泛認可和應用的是Paxos和Raft,2PC一般在前兩者幫助選主下使用。這里的一個小建議就是:如果一個分布式系統宣稱支持線性一致性級別的強一致和高可用,請先檢查它使用的一致性算法。如果是新算法,請檢查它的形式化證明或者邏輯證明。
因此,為支持線性一致性和高可用,同時完全兼容MySQL,我們在MySQL的基礎上應用Paxos,設計和開發了PhxSQL。
4、 PhxSQL的設計原則是什么?
從實際需求出發,除了前述強一致、高可用、完全兼容MySQL這3個明顯、必須的設計原則,我們還提出以下3原則。
1、簡單可邏輯證明的一致性模型
這可能是明顯區別PhxSQL和其它方案的一個特點。一個經過邏輯證明的模型才是可靠的,一個建立在可靠模型基礎上的系統也才是可信賴的。PhxSQL一致性模型建立在兩個前提上:
- Paxos保證一致性。這個大家都可以接受。
- 各個MySQL的binlog流水一致,則各個MySQL機器之間數據“一致”。這個假設小有爭議。例如即使binlog流水一致,由于不同的binlog格式、備機重放流水的不同配置,也會導致主備之間、不同備機之間的數據產生不同級別的“不同”,例如一條和當前時間相關的insert操作。這種不一致有些應用可以容忍,有些則不能。在這里,PhxSQL保證流水一致,而把格式和配置的自由留給應用和DBA根據具體場景確定。在最嚴格的配置下,binlog一致,則數據一致。
在這兩個前提下,PhxSQL的一致性模型通過Paxos,使得主機寫入Paxos的binlog流水與備機從Paxos里拉取的binlog流水一致,從而保證MySQL數據的一致性。詳細證明過程我們將另外提供。模型和證明過程都很簡單,大家在讀完源碼后也可以嘗試:)。
但即使模型正確,PhxSQL正確實現了這個模型嗎?用通俗的話講,沒有bug。碼農都知道這是個巨大的挑戰。從一個算法和模型到正確的實現之間差距是巨大的。例如,正確實現Paxos挑戰就很大。為了盡量減少bug,我們選擇了簡單和易理解同時應用廣泛的Paxos作為一致性協議。
為了實現一個“生產”級別的Paxos,三位主要碼農各自獨立實現了Paxos,在各自測試完正確性后,三套Paxos之間作為一組Paxos的獨立節點互操作檢驗正確性,最后再集體實現一個發行版本PhxPaxos!除了單元測試、系統測試外,測試環境還隨機高頻率獨立重啟機器、對網絡包進行亂序、延遲、重復等以對系統進行充分測試。
有讀者可能問,Paxos慢且網絡延遲大,PhxSQL為什么不實現一個“優化”版?Paxos有各種版本,例如Fast Paxos、EPaxos。但這些都是Paxos,遵循Paxos的基本操作,所作的改變,都做了嚴格的形式化或者邏輯證明。
PhxPaxos嚴格遵照Paxos算法實現,沒有做任何改變或者“優化”。PhxPaxos的正常寫操作的網絡延遲是一個網絡RTT,已經是任何算法理論上能達到的最快速度。
現在,對于PhxSQL來說,切換主機是一件很平常和容易的操作,就像往MySQL里插入一條數據一樣平常和容易。
2、最小侵入MySQL原則
MySQL是個巨大的快速演進的生態系統,保證PhxSQL中的MySQL與官方MySQL的兼容性與可升級性是必須的。這使得MySQL應用可以快速遷移到和運行在PhxSQL上。這也使得PhxSQL可以迅速響應官方MySQL的升級,將PhxSQL中的舊版本MySQL升級到新的官方MySQL,從而獲得新的特性、性能、穩定性、和安全性提升。
這就要求PhxSQL中的MySQL對官方MySQL改動盡量少。實際上,PhxSQL版MySQL只更改了三個小地方:修改了binlog插件接口中一個函數的參數;在MySQL啟動時新增了一個插件函數用于檢查MySQL本地的binlog文件,在MySQL協議中透傳了真正的客戶端IP以兼容授權功能(如果應用不需要可以不修改)。
這幾個改動是如此小,且涉及的是幾乎穩定不變的流程,使得PhxSQL中MySQL跟隨官方MySQL升級可以無縫完成。實際上,我們正和MySQL社區溝通,希望把這幾處修改并入官方版本,從而使得PhxSQL以后可以完全使用官方版本MySQL,也使得更多人可以方便采用PhxSQL。
(a)
(b)
圖 2:PhxSQL對MySQL的關鍵修改。(a)是MySQL。(b)是PhxSQL中的MySQL。PhxSQL修改了after_flush這個函數的參數,新增了在MySQL啟動時調用的before_recovery這個函數。
也正是因為這個原因,我們沒有因為可以減少模塊個數而把PhxSQLProxy、PhxBinlogSvr放到MySQL進程內。為了保證和應用最廣泛的MySQL單機版兼容,也沒有在事務層和存儲層介入或修改。
3、簡單的架構、部署、和運維
在滿足要求的前提下,簡單的架構有很多好處,例如開發、維護、診斷、維護、可靠性等等都變得容易。PhxSQL只有3+1個模塊。PhxSQL中的MySQL是必須的。PhxBinlogSvr負責全局binlog存儲和同步、選主、集群成員管理等關鍵功能。
在很多MySQL主備集群方案中,使用Zookeeper、etcd、心跳檢測、Agent等承擔選主的功能。PhxSQLProxy則作為傳統MySQL client訪問PhxSQL中MySQL服務的代理,使得PhxSQL的換主操作對于傳統MySQL client透明。
在其它集群方案中,一般也是通過代理MySQL proxy、或者虛擬網關VIP,向傳統MySQL client屏蔽集群的換主操作,提供透明訪問。可選模塊是PhxSQL client lib,它修改了MySQL client庫中的連接初始化函數,允許傳入一個集群的PhxSQLProxy列表,從而在一個PhxSQLProxy沒有響應時、自動訪問其它可用PhxSQLProxy,進一步提高可用性。開發人員可以直接鏈接PhxSQL client lib。
PhxSQL的部署和運維都很簡單。 在部署時,只要在目標機各自裝好PhxSQL,在配置中指定集群的機器的IP列表,PhxSQL即可運行。換主這個操作已經從運維層面轉移到PhxSQL正常的工作流程。
通常MySQL集群中換主后可能需要人肉檢查數據一致性、人肉“閃回”(這可能違反一致性保證,導致“幻讀”);無法”閃回“導致必須清除某臺MySQL,重新拉取數據備份,和追流水等。這些耗時、繁重、易錯的操作在PhxSQL中已經完全不需要。
至于熱升級和熱變更集群成員更簡單。PhxSQL支持rolling-update,可以逐步升級每臺機器。變更集群成員是指往集群添加機器、撤出機器、和替換機器(原子操作)。在保證強一致和高可用前提下,熱變更(不停服變更)是非常困難的。
PhxSQL通過在Paxos中實現成員變更解決了這個難題。PhxSQL提供了一個變更操作命令。當在新機器安裝和啟動PhxSQL后(要求MySQL已經加載一份較新的數據備份),可以在現有集群中一鍵將新機器引入、剔除一臺舊機器、或者同時做兩者。這三種操作都是原子操作!新機器會自動追流水。想想看,相當于Zookeeper支持熱變更成員,是不是很令人激動的特性?
PhxSQL由于在同步層使用Paxos,天然支持多數據中心、多機房部署,兩地三中心這種部署更是不在話下。對于PhxSQL來說,多數據中心和多機房部署與機房內部署沒有區別。PhxSQL的性能取決于多數派機器之間的網絡延遲。
5、 為什么要開源?
作為熱愛技術的碼農,我們相信開源的技術可以使得這個世界更美好。從我們日常開發使用的Emacs/Vim、GCC、GDB,間接為大眾提供社交、電子商務、信息服務的Linux、Apache、MySQL、PHP,到大眾每日使用來溝通和娛樂的Android等,開源是整個互聯網的基石,為全世界提供許多關鍵不可或缺的基礎服務。我們充分享受了開源帶來的技術進步、經濟發展、和社會前進,我們也希望開源的PhxSQL可以回饋社區,幫助更多有需要的人。
另外,我們希望通過開源更好地改進PhxSQL。我們歡迎技術性討論和志愿者提交修改。我們承諾開源的PhxSQL會一直更新。除了一些和內部運維支撐系統進行集成的功能(PhxSQL把這些功能抽象成插件,我們針對內部運維支撐系統實現了這些插件),開源版和內部版本將保持一致。
6、 PhxSQL的局限性
在一個不完美的世界里,完美是不存在的。我們很坦誠指出PhxSQL存在的兩個局限:
1、MySQL主機在執行SQL DDL命令(例如建庫和建表命令)時可能存在一致性風險。
由于MySQL的innodb引擎不支持DDL回滾,如果主機在innodb已經commit這條DDL命令,但是這條命令的binlog還沒到達PhxSQL的攔截點前宕機,則這條DDL binlog會在全局binlog中缺失,從而備機也不會收到這條binlog。
而為了保證線性一致性、serializable級別事務隔離、及“最小侵入MySQL”原則,我們也不想修改MySQL源碼,提前截獲DDL命令。考慮到DDL命令頻度較低,我們后續準備在PhxSQLProxy加入檢查和后續審計告警。也歡迎大家提出更好方案。
2、在寫入請求量很大的系統中,MySQL備機流水可能落后較多;如果這個時候主機死機,備機暫時無法提升成新主機,造成系統在一段時間內不可寫。
為了保證線性一致性,對于要求讀取最新數據的請求(通過ReadWritePort發起的讀請求)也將失敗;需要等至少一臺備機追完流水,被提升為主機才能響應讀取最新數據的請求。
對于不需要讀取最新數據的請求(通過ReadonlyPort發起的請求),可以從任意備機執行,但不保證線性一致性。(注意:PhxSQL保證無論MySQL主機流水領先MySQL備機多少,MySQL主機binlog流水和全局binlog流水是一致的,不會導致數據丟失和破壞線性一致性。)
MySQL備機追流水落后是基于binlog復制這種模式的一個潛在問題。事實上,不僅MySQL主備,任何一個多副本系統,只要每個寫操作不等待所有副本返回,都會出現類似的有些副本落后的問題;而那些等待所有副本返回的模式,在耗時和可用性方面又存在問題。
可喜的是MySQL 5.7版本實現了并行復制機制,顯著地提高了備機追流水的性能。PhxSQL將很快支持MySQL 5.7,對于寫入請求量很大的場景也可以很大程度上避免備機追流水落后的情況。
7、 Why Not?
1、為什么不支持多寫?
多寫想想就很誘人。多寫可以充分利用每臺機器寫時需要的資源。例如某些寫操作可能非常耗費CPU,多寫可以把寫操作分散在各臺機器上,充分利用各個機器的CPU資源,極大提高寫入的性能。多寫使得換主沒有存在的必要,也就沒有換主時可能存在的不可寫時間窗問題。多寫還使得客戶端可以就近寫入,減少跨數據中心寫入帶來的網絡延遲。
多寫有兩種:大家熟知的分shard或者組,各shard或者組間并行寫入,以Google Spanner[8]為典型代表;在shard或者組內并行,以Galera和MySQL Group Replication為代表。
1.組間多寫
組間多寫是把數據分成多個不相交的shard,每個組的機器負責一個shard 。當一個事務涉及的數據(讀集合和或寫集合)都在某個組時,這種事務稱為本地事務。當一個事務涉及的數據分布在超過一個組時,這種事務稱為分布式事務。
本地事務可以在本組獨立執行,組之間不需要任何通信。為了減少事務沖突帶來的性能降低,一般都是由組內leader執行本地事務,通過Paxos等一致性協議保證組內機器的數據一致[8]。各個組間并行執行本地事務,可以極大提高本地型事務的寫性能。
組間多寫最大的阻礙是分布式事務,而分布式事務是非常昂貴的。在SQL的模型中,為了實現read repeatable級別的事務隔離,事務管理器需要檢查兩個并發事務的寫數據集是否沖突;為了達到serializable級別的事務隔離,事務管理器需要檢查兩個并發事務的讀數據集和寫事務集是否沖突。
這一般通過嚴格兩階段鎖(strict two-phase locking,嚴格2PL)和/或者多版本并發控制MVCC實現。當這些數據集跨組時,就涉及到跨組的機器通信。
一個組同時遇到本地事務和分布式事務時,在本組需要根據事務的隔離級別,由事務管理器仲裁執行。
以Google Spanner為例,一個涉及兩個機器組(Spanner中的組是指Paxos組)事務就需要在coordinator leader和non-coordinator-participant leader之間兩次通信,前者組內還涉及一次Paxos寫操作,后者組內再加兩次Paxos寫操作[8,Sec. 4.2.1 Read-Write Transactions]。
當跨機房部署時,機器之間的網絡延遲使得通信代價更加高昂。Spanner為了減少這種昂貴的跨組事務,要求所有數據都必須有Primary key,并且其它數據盡量掛接在Primary key下面,使得事務盡量在一個組內、且由組內leader執行。
2.組內多主多寫
組內多主多寫時每個機器都有完整的數據,但這份數據分成不相交的邏輯集合,每個機器負責一個集合的寫入。這臺機器稱為這個集合的主機,這個集合稱為這個主機負責的數據,其它機器稱為這個集合的備機。
客戶端將寫操作發到所涉及數據的主機,由主機通過atomic broadcast原子廣播將更新請求發送給組內所有的機器,包括主機本身[9]。Galera和MySQL Group Replication都是采用這種方法。
圖 3:組內多主多寫架構[9]
原子廣播具有3個特性:
- 如果一臺機器執行一條消息所帶的更新命令,那么所有的其它機器都執行這條命令(delivered)。這里“執行”指的是原子廣播層將消息交給上層,真實的執行時刻由上層決定。在數據庫中,這個上層一般是并發事務管理器,它決定這些消息的真實執行順序。
- 所有機器以相同的順序執行命令
- 如果一臺機器成功廣播了一條消息,那么最終所有機器都將執行這條消息
使用原子廣播后,事務的生命周期從prepare->committed/aborted改變為prepare->committing->committed/aborted。
圖 4:事務生命周期狀態轉換
當一個事務只涉及到一個集合的數據時,稱為本地事務,由這個集合的主機的本地事務管理器先使用本地嚴格2PL仲裁,然后將committing狀態的事務通過原子廣播發給其它備機。
當一個事務涉及到多于一個集合的數據時,稱為復合事務(complex transaction)。
這個事務所涉及的某個數據集合的主機將committing事務狀態,包括讀集合(如果需要serializable級別隔離。這里的一個小優化是采用dummy row減少可能極其龐大的讀集合)、寫集合、以及committing狀態,通過原子廣播發給全組進行仲裁。Galera和MySQL Group Replication都只是校驗寫集合,因此不支持serializable級別事務隔離。
每臺機器都可以通過本地事務狀態和原子廣播收到的消息,獨立判定committing事務最終是提交還是終止。這種判定由原子廣播的特性保證全局一致。
圖 5:Deferred Update Replication和Certification-based Replication事務執行時序
強調一下:在機器通過原子廣播進行數據同步時,事務的最終結果不能在廣播前決定,而是在執行這條消息依賴的前置消息及這條消息后才能決定。這稱為Deferred Update Replication推遲的更新復制或者Certification-based Replication。
這里有個重要特點需要注意:只有收到一條消息的所有前置消息后,這條消息和所有未執行的前置消息才能由事務管理器并發執行。因此,這里引入了一定的串行化。
原子廣播的突出優點是在低延遲局域網有很高的吞吐率。
但同時原子廣播有不小的按機器個數放大的網絡延遲,在非低延遲網絡會顯著放大網絡延遲。MySQL Group Replication使用的Corosync和Galera支持的Spread都是基于Totem這個成員管理和原子廣播協議。
Totem是個為低延遲局域網設計的協議。在Totem中,所有機器組成一個環(ring)。無論一臺機器是否需要廣播,令牌(token)在機器之間都按照環順序傳遞。只有拿到令牌的機器才可以進行廣播,即發出提交事務請求。因此在下一臺機器收到上一臺機器令牌的網絡延遲期間,整個系統處于等待狀態。
為了保證Safe Order Delivery,消息(實際是regular token,不是regular message)需要在環中循環兩圈,才能知道是否可以執行,因此,消息延遲是(4f+3)*RTT/2,其中(2f+1)是機器的數目、f表示容錯的機器數。
在一個典型的兩地三中心部署中,這導致一次事務寫操作延遲極其高昂。例如一地兩中心的網絡延遲一般可以控制在2ms(單向),上海-深圳間網絡延遲一般是15ms(單向),則一次事務寫操作的網絡延遲是64ms!
在兩地三中心的配置中,PhxSQL的主和一個備一般分別在一地的兩中心,另一個在異地。在通常情況下,master的Paxos一次寫入只需一個accept,并且只等最快的備機返回。這時PhxSQL的寫延遲只有4ms!
相比Paxos這類協議,原子廣播還有一個缺陷。當任意一臺機器宕機或者網絡中斷時,Totem此時會超時,在踢掉宕機的機器、重新確定組成員之前,整個集群的消息停止執行,即寫操作暫停。
對于read-only事務,只有去數據集合的主機讀取、或者昂貴地讀取原子廣播Quorum臺機器、或者使用類似Spanner的TrueTime技術讀取任一符合資格的機器,才能保證線性一致性。Galera節點間有延遲,并且只讀事務在本地執行,不支持線性一致性。如果MySQL Group Replication支持線性一致性,請不吝告知。
了解基于原子廣播的組內多主多寫模式的原理和優缺點后,使用多寫模式還需要根據業務仔細劃分數據集,盡量減少公共數據的使用,同時處理好自增key的細節問題,以減少事務間的跨機沖突。
PhxSQL建立在開源的PhxPaxos基礎上,感興趣的讀者可以用PhxPaxos方便實現原子廣播插件,加載到MySQL中,從而支持多寫。
如果不要read repeatable或者serializable級別隔離的事務,例如簡單的key-value操作,同時通過lease機制保證線性一致性,是可以做到高效率多寫的。但這就違反了PhxSQL完全兼容MySQL和最小侵入MySQL的原則。
2、為什么不支持分庫分表?
分庫分表也是個誘人的選擇:可以平行無限擴展讀寫性能。分庫分表就是分組,上個小節已經討論了分布式事務的高昂成本。另外,為了保證完全兼容MySQL、支持全局事務和serializable級別事務隔離,不大改MySQL就支持sharding是非常困難的。
大改又違反了“最小侵入MySQL”原則,并且可能引入新的不兼容性。在應用不要求全局事務和serializable級別事務隔離情況下,感興趣的讀者可以把PhxSQL作為一容錯的MySQL模塊,在上層構建支持分庫分表的系統。因為PhxSQL本身的容錯性,這樣做比在MySQL基礎上直接構建要簡單,無需關心每個sharding本身的出錯。
如果以后有需求,PhxSQL團隊也可能基于PhxSQL開發一個分庫分表的新產品。當然,這個產品難以提供PhxSQL級別的兼容性。
3、為什么這么糾結于serializable級別事務隔離性,read repeatable級別很多時候已經夠用了啊?
我們在設計原則中已經提到,為了完全兼容MySQL。我們認為一項好的技術是一項簡單方便用戶的技術,提供符合用戶直覺預期、不用看太多注意事項的技術是我們的體貼。我們很誠懇,也是為了方便用戶。我們不想說PhxSQL完全兼容MySQL,然后在不起眼的地方blabla列出好幾頁蠅頭小字的例外。事實上,對于關鍵業務來說,serializable是必要的、read repeatable是不足的。read repeatable有個令人討厭的write-skew異常[12]。
舉個例子。小薇在一個銀行有兩張信用卡,分別是A和B。銀行給這兩張卡總的信用額度是2000,即A透支的額度和B透支的額度相加必須不大于2000:A+B<=2000。
兩個賬戶的扣款函數用事務執行分別是:
A賬戶扣款函數:
sub_A(amount_a):
begin transaction
if (A+B+amount_a <= 2000)
{ A += amount_a }
Commit
B賬戶扣款函數:
sub_B(amount_b):
begin transaction
if (A+B+amount_b <= 2000)
{ B += amount_b }
commit
假定現在A==1000,B==500。如果小薇是個黑客,同時用A賬戶消費300和B賬戶消費300,即amount_a == 400,amount_b == 300。那么這個數據庫會發生什么事情呢?
如果是read repeatable級別隔離,sub_a和sub_b都會同時成功!最后A和B賬戶的透支額分別是A=1000+400=1400,B=500+300=800,總的透支額A+B=1400+800=2200>2000,超過了銀行授予的額度!如果不是信用卡的兩筆小消費,而是兩筆大額轉賬,那么銀行怎么辦?
如果是serializable級別隔離,則sub_a和sub_b只有一個成功。具體分析有興趣的讀者可以自己完成。
4、為什么不把顯著提升MySQL性能作為一個主要目標?
事實上,PhxSQL已經顯著提升了MySQL主備的寫入性能。與semi-sync比,在測試環境中,PhxSQL的寫入性能比semi-sync高15%到20%以上。讀性能持平。這是在滿足完全兼容MySQL和最小侵入MySQL原則下所能獲得的結果。
鑒于PhxSQL對MySQL的改動是如此之小,對性能有高要求的讀者,可以方便地把PhxSQL中的MySQL換成其它高性能版本,獲得更高性能。
5、為什么編譯時不支持C++11以下標準?
作為熱愛新技術的碼農,我們真的很喜歡C++11中期待已久、激動人心的新特性,例如極大增強的模板、lambda表達式、右值和move表達式、多線程內存模型等,這將C++帶入了一個新的時代,大大提高了碼農搬磚的速度、編碼的正確性、和程序的性能。有了C++11,PhxSQL的開發效率提高了很多。
8、與Galera及MySQL Group replication的比較
參見7.1.2小節。
結論
PhxSQL是一個完全兼容MySQL,提供與Zookeeper相同強一致性和可用性的MySQL集群。
來自:http://www.yunweipai.com/archives/9682.html