在分佈式數據庫、雲原生數據庫、NewSQL等名詞在數據庫領域層出不窮的當今,變革——在這個相對穩定的領域已愈加不可避免。相比於完全革新,漸進式增強的方案在擁有厚重沉澱的行業則更受青睞。
同所有分佈式領域的解決方案相同,分而治之的透明化數據分片方案,是新一代數據庫解決海量數據的核心理念。水平拆分使得分佈式事務的重要性,較之垂直拆分的業務系統進一步提升。另外,彈性擴(縮)容、HTAP等概念也是新一代數據庫的關注重點。京東數科開源的Apache ShardingSphere在數據分片方面已逐漸成熟,在此場景之上開發的分佈式事務中間件JDTX與之共同組成了分佈式數據庫的內核拼圖。
JDTX是由京東數科的數據研發團隊傾力打造的分佈式事務中間件。本次分享是JDTX首次公開出現在大衆視野面前,分享內容涵蓋JDTX的核心設計理念及相關的技術實現難點,希望能爲打造分佈式事務解決方案的團隊提供一些思路。
背景
數據庫事務需要滿足ACID(原子性、一致性、隔離性、持久性)4個特性。
在單一數據節點中,事務僅限於對單一數據庫資源的訪問控制,稱之爲本地事務。幾乎所有的成熟的關係型數據庫都提供了對本地事務的原生支持。 但是在基於微服務的分佈式應用環境下,越來越多的應用場景要求對多個服務的訪問及其相對應的多個數據庫資源能納入到同一個事務當中,分佈式事務應運而生。
關係型數據庫雖然對本地事務提供了完美的ACID原生支持。 但在分佈式的場景下,它卻成爲系統性能的桎梏。如何讓數據庫在分佈式場景下滿足ACID的特性或找尋相應的替代方案,是分佈式事務的重點工作。
本地事務
在不開啓任何分佈式事務管理器的前提下,讓每個數據節點各自管理自己的事務。 它們之間沒有協調以及通信的能力,也並不互相知曉其他數據節點事務的成功與否。 本地事務在性能方面無任何損耗,但在強一致性以及最終一致性方面則力不從心。
兩階段提交
XA協議最早的分佈式事務模型是由X/Open國際聯盟提出的X/Open Distributed Transaction Processing(DTP)模型,簡稱XA協議。
基於XA協議實現的分佈式事務對業務侵入很小。 它最大的優勢就是對使用方透明,用戶可以像使用本地事務一樣使用基於XA協議的分佈式事務。 XA協議能夠嚴格保障事務ACID特性。
嚴格保障事務ACID特性是一把雙刃劍。 事務執行在過程中需要將所需資源全部鎖定,它更加適用於執行時間確定的短事務。 對於長事務來說,整個事務進行期間對數據的獨佔,將導致對熱點數據依賴的業務系統併發性能衰退明顯。 因此,在高併發的性能至上場景中,基於XA協議兩階段提交類型的分佈式事務並不是最佳選擇。
柔性事務
如果將實現了ACID的事務要素的事務稱爲剛性事務的話,那麼基於BASE事務要素的事務則稱爲柔性事務。 BASE是基本可用、柔性狀態和最終一致性這3個要素的縮寫。
在ACID事務中對一致性和隔離性的要求很高,在事務執行過程中,必須將所有的資源佔用。 柔性事務的理念則是通過業務邏輯將互斥鎖操作從資源層面上移至業務層面。通過放寬對強一致性和隔離性的要求,只要求當整個事務最終結束的時候,數據是一致的。而在事務執行期間,任何讀取操作得到的數據都有可能被改變。這種弱一致性的設計可以用來換取系統吞吐量的提升。Saga和TCC都是典型的柔性事務實現方案。
結論
基於ACID的兩階段事務和基於BASE的最終一致性事務都不是銀彈,可通過下表詳細對比它們之間的區別。
兩階段提交 | 柔性事務 | |
---|---|---|
業務改造 | 無 | 實現相關接口 |
一致性 | 支持 | 最終一致 |
隔離性 | 支持 | 業務方保證 |
併發性能 | 嚴重衰退 | 略微衰退 |
適合場景 | 短事務 & 低併發 | 長事務 & 高併發 |
缺乏併發度保障的兩階段事務不能稱之爲完善的分佈式事務解決方案;而缺乏對ACID原義支持的柔性事務都甚至不能稱之爲數據庫事務,它更適合服務層的事務處理。
放眼當前,實難找到無需權衡即可放之四海而皆準的分佈式事務解決方案。
JDTX的分佈式事務解決方案
JDTX的設計目標是強一致(支持ACID的事務原義)、高性能(甚至強於本地事務)、1PC(完全摒棄兩階段提交和兩階段鎖)的完全分佈式事務中間件,目前可用於關係型數據庫。它採用完全開放SPI的設計方式,提供與NoSQL對接的可能,能夠將多元異構數據維持在同一事務中。
設計理念
首先通過一張架構圖來直觀的瞭解一下JDTX的構成。
JDTX由事務管理器(TM)和資源管理器(RM)組成。
事務管理器用於生成全局單調遞增的事務日誌序列號(LSN),事務的提交和回滾等核心流程處理,以及未提交事務的本地元祖(Tuple)持有。
資源管理器用於管理活躍事務數據。JDTX的設計特點是將在事務中的數據(稱之爲活躍數據)和不在事務中的數據(稱之爲落盤數據)分離。活躍數據在落盤至預寫日誌系統(WAL)之後,並將數據保存至自研的多版本快照(MVCC)內存引擎中。落盤數據則是通過異步刷盤的方式,將MVCC引擎中的數據以流量可控的方式同步至最終的存儲介質中(如:關係型數據庫)。
事務內查詢會將落盤數據和活躍數據合併,並根據當前事務的隔離級別獲取出符合當前事務可見性的數據版本。
方案亮點
無損事務方案
JDTX採用WAL + MVCC引擎的方式實現了事務的ACID原義。
原子性&一致性支持
JDTX的 MVCC引擎可以看做是一個集中式緩存,可以將兩階段提交簡化至一階段提交。維持單一節點中數據的原子性和一致性,即將分佈式事務的範疇縮減到本地事務的範疇。
MVCC引擎可以通過分片 + 主從同步的方式維持水平擴展和高可用的能力。JDTX保證所有對事務數據的訪問都通過MVCC引擎的活躍數據 + 最終數據端的落盤數據的合併的方式,以保證數據的原子性和一致性。
隔離性支持
JDTX以多版本快照的方式實現事務隔離性。目前完整的支持4種標準隔離級別中的讀已提交和可重複讀,已經可以滿足絕大部分需求。
持久性支持
JDTX將事務的活躍數據在存入MVCC引擎之前先落盤至WAL引擎,以保證服務器崩潰,內存數據丟失時,活躍數據依然能夠從WAL引擎中完全恢復。
高性能
JDTX採用將活躍數據異步刷盤至數據庫的方式極大的提高了數據寫入的性能上限。它的性能瓶頸從數據庫寫入耗時轉移到了落盤至WAL引擎和存儲至MVCC引擎的耗時。
與數據庫的WAL系統類似,JDTX的WAL也採用日誌順序追加的方式,因此可以簡單的理解爲JDTX的WAL耗時 = 數據庫系統的WAL耗時。而MVCC緩存則採用哈希數據結構,其寫入耗時小於需要維護BTree索引的數據庫寫入耗時。因此,採用JDTX的事務方案,其數據更新性能甚至強於不開啓事務。
另外,JDTX採取了無UNDO日誌的事務回滾策略。未提交的數據並不會進入MVCC引擎,而是被事務管理器本地持有。因此,只要清理掉未提交數據即可完成事務回滾。無UNDO日誌的設計進一步的提升了事務處理的性能。
高可用
WAL引擎和MVCC引擎均採用分片 + 主備的方式,以保證JDTX不會產生單點故障。在MVCC引擎完全不可用的情況下,可通過恢復模式將WAL中的數據同步至數據庫,以保證數據的完整性。
跨多元數據庫事務
JDTX將事務活躍數據和落盤數據分離的設計方案,使其落盤數據存儲端無任何限制。所有的事務活躍數據都會通過異步的落盤執行器存儲至後端數據庫,因此後端是否爲同構數據庫,其實並無影響。
使用JDTX能夠保證跨多元存儲端(如:MySQL、PostgreSQL甚至是MongoDB、Redis等NoSQL)的分佈式事務維持在同一事務語義之中。
實現難點
MVCC內核
事務隔離級別有兩種常見的實現方案,即鎖實現和MVCC實現。除了Infomix等少數數據庫,大部分關係型數據庫均採用MVCC實現。
讀未提交、讀已提交、可重複讀和可序列化這4種事務隔離級別的標準,是ANSI所定義的基於鎖實現的方式。事務的並行度隨着隔離級別的增加而衰減,除了併發度最低的可序列化,其他隔離級別都伴隨着對一致性的權衡和犧牲。
下表是基於鎖實現的隔離級別對照表。
隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
讀未提交 | 可能 | 可能 | 可能 |
讀已提交 | 不可能 | 可能 | 可能 |
可重複讀 | 不可能 | 不可能 | 可能 |
可序列化 | 不可能 | 不可能 | 不可能 |
通過MVCC實現的隔離級別實際上只有SI(快照隔離)和SSI(可序列化快照隔離)這2種。SI和SSI與ANSI的4種隔離級別並不能完全對照。其中的讀未提交,與讀已提交在MVCC的實現中性能並無差別,可以忽略不計。因此SI可以對應爲讀已提交和可重複讀這2種隔離級別。實際上,即使是幻讀,在SI隔離級別中也是不會出現的。
由於快照併發控制並不能真正意義上保證事務是“可串行化”的,所以事務間的併發操作依舊有可能引發數據異常現象。但這裏的異常不同於之前提到的髒讀、丟失更新的異常,而是一種業務數據間邏輯語義層面的異常,也可以說是由於未能滿足數據間的語義約束而產生的異常。這被稱之爲寫偏序(Write skew),它的檢測可依據併發事務間讀寫依賴的多版本可串行化圖(The multiversion serialization graph)來實現,即SSI隔離級別。
下表是基於MVCC實現的隔離級別對照表。
隔離級別 | 髒讀 | 不可重複讀 | 幻讀 | 寫偏序 |
---|---|---|---|---|
讀未提交 | 無需實現 | 無需實現 | 無需實現 | 無需實現 |
讀已提交 | 不可能 | 可能 | 可能 | 可能 |
可重複讀 | 不可能 | 不可能 | 不可能 | 可能 |
可序列化 | 不可能 | 不可能 | 不可能 | 不可能 |
自研MVCC引擎是JDTX的主要難點之一。JDTX採用與PostgreSQL類似的MVCC實現方案,通過xmin和xmax標記事務快照範圍,並在MVCC引擎中保存每個數據元祖(Tuple)的xmin和xmax的事務信息。同一數據的多版本以鏈表的數據結構存儲,通過其xmin和xmax來獲取數據的版本在當前事務快照中的可見性。
由於MySQL也並未實現SSI隔離級別,因此目前的JDTX只是實現了SI隔離級別,還並未實現SSI隔離級別。
MVCC數據的清理(vacuum)是另一技術難點。過長的事務會導致MVCC版本過多,導致佔用大量存儲空間。尤其是JDTX是通過內存來存儲MVCC的活躍數據,因此對內存空間的釋放則更加敏感。由於JDTX的異步落盤機制,因此除了MVCC標準的垃圾回收邏輯之外,判斷數據是否落盤成爲清理邏輯的額外規則。
SQL查詢引擎
通過SQL查詢事務的活躍數據,是JDTX的另一個技術實現難點。MVCC引擎並非關係型數據庫,並不能通過識別SQL來查詢相關數據。JDTX則通過之前Apache ShardingSphere所積累的SQL解析模塊及其抽象語法樹(AST)來實現對SQL的理解,以及查詢基於內存的MVCC引擎中的數據。
對於SPJ(select-project-join)的OLTP類型SQL,可以從SQL的查詢結果中獲取數據主鍵。JDTX將落盤數據從後端數據庫中取出作爲最終展現數據的基礎,並在此之上從MVCC引擎中查詢出當前事務可見的活躍數據,並對其結果進行歸併。換句話說,每次事務內查詢都是由落盤數據+活躍數據歸併而成。歸併引擎部分參照了LSM Tree的設計思想。
對於非SPJ的OLAP類型SQL,JDTX則採用另外的查詢方式。基於聚合函數和分組的SQL無法通過主鍵直接將後端數據庫中的落盤數據和MVCC引擎中的鍵值數據直接匹配,因此採用以MVCC引擎中數據爲主,並將SQL改寫爲剔除活躍數據主鍵的新SQL,再從後端數據庫中查詢無重複的聚合數據進行歸併。
使用限制
分佈式無銀彈,這是架構師們對現有的分佈式系統比較公認的看法。雖然JDTX具備了很多優點,但仍然有一些使用限制。它的使用限制主要有以下3點。
-
需要通過JDTX訪問數據庫。JDTX通過其MVCC引擎控制事務的原子性、一致性和隔離性,並通過WAL控制事務的持久性。因此在使用JDTX的系統中,跨過事務中間件直接查詢數據庫,是得不到正確的事務數據的,修改數據庫則會導致數據紊亂。
-
SQL支持需要持續完善。查詢MVCC引擎的SQL方言兼容則需要持續提升。相對於無損的ACID事務原義支持所帶來的優勢,SQL兼容度的下降,是JDTX帶來的權衡。
-
不支持無主鍵數據。JDTX需要通過主鍵來合併MVCC引擎和數據庫中的數據。因此無法處理沒有主鍵的記錄。
JDTX與Apache ShardingSphere
通過Apache ShardingSphere提供的JDBC接入端,可以使JDTX無縫的對接至Java應用。除了JDBC接入端,Apache ShardingSphere也提供了基於MySQL和PostgreSQL的Proxy接入端,使JDTX像一個單獨的數據庫一樣提供分佈式事務的服務。Apache ShardingSphere將在未來將接入端剝離,使JDTX獨立使用成爲可能。
Apache ShardingSphere提供了分佈式事務的統一SPI。JDTX通過實現ShardingSphere提供的SPI,可以很輕鬆的融入Apache ShardingSphere生態。結合Apache ShardingSphere與JDTX,可以將數據分片與分佈式事務無縫結合。
獨立使用Apache ShardingSphere或JDTX,可以靈活解耦,高度定製,可以看做是基礎組件的樂高積木。而將其聯合使用,則能夠產生化學變化,甚至使它們具備組成分佈式數據庫基礎設施的能力。架設在產品最前端的Apache ShardingSphere用於SQL解析、數據庫協議和數據分片;位於中層的JDTX用於通過鍵值對和MVCC的方式處理事務活躍數據;最底層的數據庫則僅作爲最終的數據存儲端。下圖是ShardingSphere + JDTX的架構圖。
最後附上MySQL架構圖,請讀者自行體會其相似之處。
JDTX的後續規劃
JDTX的自身目標是力爭將其打造成爲一個分佈式事務的標準解決方案。在事務核心流程、MVCC引擎、WAL引擎、高可用等核心功能打磨成熟後,JDTX會將主要精力投放在以下幾個方面:
-
提升SQL語句兼容性以及多元數據庫支持;
-
實現SSI隔離級別,提供完整的MVCC隔離級別解決方案;
-
完善管理端和監控端。
除了JDTX中間件自身,它也將與ShardingSphere等其他數據庫中間件更加一體化的提供分佈式數據庫級別的服務;並將與Kubernetes等雲原生平臺更加深度整合,爲雲原生數據庫提供服務。
作者介紹
張亮,京東數科數據研發負責人,Apache ShardingSphere發起人 & PPMC,JDTX負責人。
熱愛開源,主導開源項目ShardingSphere(原名Sharding-JDBC)和Elastic-Job。擅長以Java爲主分佈式架構,推崇優雅代碼,對如何寫出具有展現力的代碼有較多研究。
目前主要精力投入在將ShardingSphere和JDTX打造爲業界一流的金融級數據解決方案之上。ShardingSphere已經進入Apache孵化器,是京東集團首個進入Apache基金會的開源項目,也是Apache基金會首個分佈式數據庫中間件。
GitHub: https://github.com/terrymanu, 隨時歡迎技術交流和指正。