談談PhxSQL的設計和實現哲學(下)
開源地址
GitHub - tencent-wechat/phxsql: A high availability MySQL cluster that guarantees data consistency between a master and slaves.
上章鏈接
談談PhxSQL的設計和實現哲學(上) - 陳明的文章 - 知乎專欄
摘要
上一章討論了我們為什麼要做PhxSQL和為什麼這樣做PhxSQL。這裡我們主要談談為什麼不做某些特性。捨得捨得,有舍才有得。CAP告訴我們只能三選二,俗話告訴我們天下沒有免費的午餐。每個特性除了自身提供的功能,也有其代價。為了保證強一致的線性一致性、高可用、serializable級別事務隔離、完全兼容MySQL、和最小侵入MySQL,PhxSQL放棄了一些特性。
正文
7. Why Not?
7.1. 為什麼不支持多寫?
多寫想想就很誘人。多寫可以充分利用每台機器寫時需要的資源。例如某些寫操作可能非常耗費CPU,多寫可以把寫操作分散在各台機器上,充分利用各個機器的CPU資源,極大提高寫入的性能。多寫使得換主沒有存在的必要,也就沒有換主時可能存在的不可寫時間窗問題。多寫還使得客戶端可以就近寫入,減少跨數據中心寫入帶來的網路延遲。
多寫有兩種:大家熟知的分shard或者組,各shard或者組間並行寫入,以Google Spanner[8]為典型代表;在shard或者組內並行,以Galera和MySQL Group Replication為代表。
7.1.1. 組間多寫
組間多寫是把數據分成多個不相交的shard,每個組的機器負責一個shard 。當一個事務涉及的數據(讀集合和或寫集合)都在某個組時,這種事務稱為本地事務。當一個事務涉及的數據分布在超過一個組時,這種事務稱為分散式事務。
本地事務可以在本組獨立執行,組之間不需要任何通信。為了減少事務衝突帶來的性能降低,一般都是由組內leader執行本地事務,通過Paxos等一致性協議保證組內機器的數據一致[8]。各個組間並行執行本地事務,可以極大提高本地型事務的寫性能。
組間多寫最大的阻礙是分散式事務,而分散式事務是非常昂貴的。在SQL的模型中,為了實現read repeatable級別的事務隔離,事務管理器需要檢查兩個並發事務的寫數據集是否衝突;為了達到serializable級別的事務隔離,事務管理器需要檢查兩個並發事務的讀數據集和寫事務集是否衝突[10]。這一般通過嚴格兩階段鎖(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執行。
7.1.2. 組內多主多寫
組內多主多寫時每個機器都有完整的數據,但這份數據分成不相交的邏輯集合,每個機器負責一個集合的寫入。這台機器稱為這個集合的主機,這個集合稱為這個主機負責的數據,其它機器稱為這個集合的備機。客戶端將寫操作發到所涉及數據的主機,由主機通過atomic broadcast原子廣播將更新請求發送給組內所有的機器,包括主機本身[9]。Galera和MySQL Group Replication都是採用這種方法。
圖 3:組內多主多寫架構[9]
原子廣播具有3個特性:
i) 如果一台機器執行一條消息所帶的更新命令,那麼所有的其它機器都執行這條命令(delivered)。這裡「執行」指的是原子廣播層將消息交給上層,真實的執行時刻由上層決定。在資料庫中,這個上層一般是並發事務管理器,它決定這些消息的真實執行順序。
ii) 所有機器以相同的順序執行命令。
iii) 如果一台機器成功廣播了一條消息,那麼最終所有機器都將執行這條消息。
使用原子廣播後,事務的生命周期從prepare->committed/aborted改變為prepare->committing->committed/aborted。
圖 4:事務生命周期狀態轉換[9]
當一個事務只涉及到一個集合的數據時,稱為本地事務,由這個集合的主機的本地事務管理器先使用本地嚴格2PL仲裁,然後將committing狀態的事務通過原子廣播發給其它備機。
當一個事務涉及到多於一個集合的數據時,稱為複合事務(complex transaction)。這個事務所涉及的某個數據集合的主機將committing事務狀態,包括讀集合(如果需要serializable級別隔離。這裡的一個小優化是採用dummy row減少可能極其龐大的讀集合[10])、寫集合、以及committing狀態,通過原子廣播發給全組進行仲裁。Galera和MySQL Group Replication都只是校驗寫集合,因此不支持serializable級別事務隔離[18]。
每台機器都可以通過本地事務狀態和原子廣播收到的消息,獨立判定committing事務最終是提交還是終止。這種判定由原子廣播的特性保證全局一致。
圖 5:Deferred Update Replication和Certification-based Replication事務執行時序[11]
強調一下:在機器通過原子廣播進行數據同步時,事務的最終結果不能在廣播前決定,而是在執行這條消息依賴的前置消息及這條消息後才能決定。這稱為Deferred Update Replication[9][14]推遲的更新複製或者Certification-based Replication[11]。這裡有個重要特點需要注意:只有收到一條消息的所有前置消息後,這條消息和所有未執行的前置消息才能由事務管理器並發執行。因此,這裡引入了一定的串列化。
原子廣播的突出優點是在低延遲區域網有很高的吞吐率。
但同時原子廣播有不小的按機器個數放大的網路延遲,在非低延遲網路會顯著放大網路延遲。MySQL Group Replication使用的Corosync[16]、Galera[15]默認使用的gcomm和支持的Spread[17]都是基於Totem[13]這個成員管理和原子廣播協議。
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[8]技術讀取任一符合資格的機器,才能保證線性一致性。Galera節點間有延遲,並且只讀事務在本地執行,不支持線性一致性[15]。如果MySQL Group Replication支持線性一致性,請不吝告知。
了解基於原子廣播的組內多主多寫模式的原理和優缺點後,使用多寫模式還需要根據業務仔細劃分數據集,盡量減少公共數據的使用,同時處理好自增key等細節問題,以減少事務間的跨機衝突。
PhxSQL建立在開源的PhxPaxos基礎上,感興趣的讀者可以用PhxPaxos方便實現原子廣播插件,載入到MySQL中,從而支持多寫。
如果不要read repeatable或者serializable級別隔離的事務,例如簡單的key-value操作,同時通過lease機制保證線性一致性,是可以做到高效率多寫的。但這就違反了PhxSQL完全兼容MySQL和最小侵入MySQL的原則。
7.2. 為什麼不支持分庫分表?
分庫分表也是個誘人的選擇:可以平行無限擴展讀寫性能。分庫分表就是分組,上個小節已經討論了分散式事務的高昂成本。另外,為了保證完全兼容MySQL、支持全局事務和serializable級別事務隔離,不大改MySQL就支持sharding是非常困難的。大改又違反了「最小侵入MySQL」原則,並且可能引入新的不兼容性。在應用不要求全局事務和serializable級別事務隔離情況下,感興趣的讀者可以把PhxSQL作為一容錯的MySQL模塊,在上層構建支持分庫分表的系統。因為PhxSQL本身的容錯性,這樣做比在MySQL基礎上直接構建要簡單,無需關心每個sharding本身的出錯。如果以後有需求,PhxSQL團隊也可能基於PhxSQL開發一個分庫分表的新產品。當然,這個產品難以提供PhxSQL級別的兼容性。
7.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賬戶消費400和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只有一個成功。具體分析有興趣的讀者可以自己完成。
7.4. 為什麼不把顯著提升MySQL性能作為一個主要目標?
事實上,PhxSQL已經顯著提升了MySQL主備的寫入性能。與semi-sync比,在測試環境中,PhxSQL的寫入性能比semi-sync高16%到25%以上。讀性能持平。這是在滿足完全兼容MySQL和最小侵入MySQL原則下所能獲得的結果。鑒於PhxSQL對MySQL的改動是如此之小,對性能有高要求的讀者,可以方便地把PhxSQL中的MySQL換成其它高性能版本,獲得更高性能。
7.5. 為什麼編譯時不支持C++11以下標準?
作為熱愛新技術的碼農,我們真的很喜歡C++11中期待已久、激動人心的新特性,例如極大增強的模板、lambda表達式、右值和move表達式、多線程內存模型等,這將C++帶入了一個新的時代,大大提高了碼農搬磚的速度、編碼的正確性、和程序的性能。有了C++11,PhxSQL的開發效率提高了很多。
8. 與Galera及MySQL Group replication的比較
參見7.1.2小節。
結論
PhxSQL是一個完全兼容MySQL,提供與Zookeeper相同強一致性和可用性的MySQL集群。
感謝大家看完這麼長的一篇文章。希望大家多閱讀PhxSQL源碼,多提技術性意見,甚至成為源碼貢獻者!
參考
1. M.P. Herlihy and J. M. Wing. Linearizability: a correctness condition for concurrent objects. ACM Transactions on Programming Languages and Systems (TOPLAS), Volume 12 Issue 3, July 1990, Pages 463-492.
2. L. Lamport. How to make a multiprocessor computer that correctly executes multiprocess programs. IEEE Trans. Computer. C-28,9 (Sept. 1979), 690-691.
3. P. Hunt, M. Konar, F. P. Junqueira, and B. Reed. ZooKeeper: wait-free coordination for Internet-scale systems. USENIXATC10, 2010.
4. L. Lamport. Paxos Made Simple. ACM SIGACT News (Distributed Computing Column) 32, 4 (Whole Number 121, December 2001) 51-58.
5. D. Ongaro and J. Ousterhout. In search of an understandable consensus algorithm. USENIX ATC 』14, 2014.
6. B. M. Oki and B.H. Liskov. Viewstamped replication: a New primary copy method to support highly-available distributed systems. PODC』88, 8-17, 1988.
7. T. Chandra, R. Griesemer, and J. Redstone. Paxos made live – an engineering perspective. PODC』07, 2007.
8. J. C. Corbett, J. Dean, M. Epstein, and etc. Spanner: Googles Globally-Distributed Database. OSDI12, 2012.
9. F. Pedone, R. Guerraoui, and A. Schiper. The database state machine approach. Journal of Distributed and Parallel Databases and Technology, 14:71–98, 2002
10. V. Zuikeviciute and F. Pedone. Revisiting the database state machine approach. VLDB Workshop on Design, Implementation, and Deployment of Database Replication. 2005.
11. Certification-based Replication. Visited at 2016/9/5.
12. Snapshot isolation. Visited at 2016/9/5.
13. Y. Amir, L. E. Moser, P. M. Melliar-smith, D. A. Agarwal, and P. Ciarfella. The totem single ring ordering and membership protocol. ACM Transactions on Computer Systems. 13 (4): 311–342.
14. http://downloads.mysql.com/presentations/innovation-day-2016/Session_7_MySQL_Group_Replication_for_High_Availability.pdf. Visited at 2016/9/5.
15. Replication API. Visited at 2016/9/5.
16. Corosync by corosync. Visited at 2016/9/5.
17. http://www.spread.org/. Visited at 2016/9/5.
18. Isolation Levels. Visited at 2016/9/5
19. L. Lamport. Fast Paxos. Technical Report, MSR-TR-2005-112.
20. I. Moraru, D. G. Andersen, and M. Kaminsky. There is more consensus in egalitarian parliaments. SOSP』13, 2013.
21. GitHub - tencent-wechat/phxpaxos: C++ Paxos library that has been used in Wechat production environment..
22. 【重磅】微信開源PhxSQL:高可用、強一致的MySQL集群. Visited at 2016/9/5.
推薦閱讀:
※怎樣理解王爾德的【談論天氣是無趣人類最後的避難所】這句話?
※Linearizability 一致性驗證
TAG:分布式 | 一致性 | MySQLCluster |