首次公开:京东数科强一致、高性能分布式事务中间件 JDTX
來源:https://www.infoq.cn/article/BAXzcfjRTcgmKisa7JHm
在分布式數據庫、云原生數據庫、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 隔離級別中也是不會出現的。
由于快照并發控制并不能真正意義上保證事務是“可串行化”的,所以事務間的并發操作依舊有可能引發數據異?,F象。但這里的異常不同于之前提到的臟讀、丟失更新的異常,而是一種業務數據間邏輯語義層面的異常,也可以說是由于未能滿足數據間的語義約束而產生的異常。這被稱之為寫偏序(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 則采用另外的查詢方式?;诰酆虾瘮岛头纸M的 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,?隨時歡迎技術交流和指正。
總結
以上是生活随笔為你收集整理的首次公开:京东数科强一致、高性能分布式事务中间件 JDTX的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是 Java 对象深拷贝?面试必问!
- 下一篇: 厉害了,淘宝千万并发,14 次架构演进…