本文整理自螞蟻金服技術專家、分佈式事務 Seata 發起者之一張森(花名:紹輝)在 GIAC 全球互聯網架構大會的分享。詳細講解了在分佈式架構演進中,螞蟻金服面對的跨服務、跨數據庫的業務數據一致性問題以及應對措施,並分享了分佈式事務 Seata 的 AT、TCC、Saga 和 XA 四種模式。git
Seata:github.com/seata/seatagithub
螞蟻金服的業務數據庫起初是單庫單表,但隨着業務數據規模的快速發展,數據量愈來愈大,單庫單表逐漸成爲瓶頸。因此咱們對數據庫進行了水平拆分,將原單庫單表拆分紅數據庫分片。數據庫
以下圖所示,分庫分表以後,原來在一個數據庫上就能完成的寫操做,可能就會跨多個數據庫,這就產生了跨數據庫事務問題。安全
在業務發展初期,「一塊大餅」的單業務系統架構,能知足基本的業務需求。可是隨着業務的快速發展,系統的訪問量和業務複雜程度都在快速增加,單系統架構逐漸成爲業務發展瓶頸,解決業務系統的高耦合、可伸縮問題的需求愈來愈強烈。性能優化
以下圖所示,螞蟻金服按照面向服務(SOA)的架構的設計原則,將單業務系統拆分紅多個業務系統,下降了各系統之間的耦合度,使不一樣的業務系統專一於自身業務,更有利於業務的發展和系統容量的伸縮。網絡
業務系統按照服務拆分以後,一個完整的業務每每須要調用多個服務,如何保證多個服務間的數據一致性成爲一個難題。架構
在數據庫水平拆分、服務垂直拆分以後,一個業務操做一般要跨多個數據庫、服務才能完成。在分佈式網絡環境下,咱們沒法保障全部服務、數據庫都百分百可用,必定會出現部分服務、數據庫執行成功,另外一部分執行失敗的問題。框架
當出現部分業務操做成功、部分業務操做失敗時,業務數據就會出現不一致。以金融業務中比較常見的「轉帳」場景爲例:異步
以下圖所示,在支付寶的「轉帳」操做中,要分別完成 4 個動做:分佈式
而完成以上操做要分別訪問 3 個服務和 4 個數據庫。
在分佈式環境下,確定會出現部分操做成功、部分操做失敗的問題,好比:A 帳戶的錢扣了,可是 B 帳戶的錢沒加上,這就形成了資金損失,影響資金安全。
在金融業務場景下,咱們必須保證「轉帳」的原子性,要麼全部操做所有成功,要麼所有失敗,不容許出現部分紅功部分失敗的現象。
爲了解決跨數據庫、跨服務的業務數據一致性問題,螞蟻金服自主研發了分佈式事務中間件。
從 2007 年開始作分佈式事務並支持雙十一,至今已經有 12 年。
2013 年,螞蟻金服開始作單元化改造,分佈式事務也開始支持 LDC、異地多活和高可用容災,解決了機房故障狀況下服務快速恢復的問題。
2014 年,螞蟻金服分佈式事務中間件 DTX(Distributed Transaction-eXtended)開始經過螞蟻金融雲對外輸出,咱們發展了一大批的外部用戶。在發展外部客戶的過程當中,外部客戶表示願意犧牲一部分性能(無螞蟻的業務規模)以換取接入便利性和無侵入性。因此在 2015 年,咱們開始作無侵入的事務解決方案:FMT 模式和 XA 模式。
螞蟻金服分佈式事務(Distributed Transaction-eXtended,簡稱 DTX)連接:
Seata(Simple Extensible Autonomous Transaction Architecture,簡單可擴展自治事務框架)是 2019 年 1 月份螞蟻金服和阿里巴巴共同開源的分佈式事務解決方案。Seata 開源半年左右,目前已經有接近一萬 star,社區很是活躍。咱們熱忱歡迎你們參與到 Seata 社區建設中,一同將 Seata 打形成開源分佈式事務標杆產品。
Seata:https://github.com/seata/seata
以下圖所示,Seata 中有三大模塊,分別是 TM、RM 和 TC。 其中 TM 和 RM 是做爲 Seata 的客戶端與業務系統集成在一塊兒,TC 做爲 Seata 的服務端獨立部署。
在 Seata 中,分佈式事務的執行流程:
Seata 會有 4 種分佈式事務解決方案,分別是 AT 模式、TCC 模式、Saga 模式和 XA 模式。
今年 1 月份,Seata 開源了 AT 模式。AT 模式是一種無侵入的分佈式事務解決方案。在 AT 模式下,用戶只需關注本身的「業務 SQL」,用戶的 「業務 SQL」 做爲一階段,Seata 框架會自動生成事務的二階段提交和回滾操做。
在一階段,Seata 會攔截「業務 SQL」,首先解析 SQL 語義,找到「業務 SQL」要更新的業務數據,在業務數據被更新前,將其保存成「before image」,而後執行「業務 SQL」更新業務數據,在業務數據更新以後,再將其保存成「after image」,最後生成行鎖。以上操做所有在一個數據庫事務內完成,這樣保證了一階段操做的原子性。
二階段若是是提交的話,由於「業務 SQL」在一階段已經提交至數據庫, 因此 Seata 框架只需將一階段保存的快照數據和行鎖刪掉,完成數據清理便可。
二階段若是是回滾的話,Seata 就須要回滾一階段已經執行的「業務 SQL」,還原業務數據。回滾方式即是用「before image」還原業務數據;但在還原前要首先要校驗髒寫,對比「數據庫當前業務數據」和 「after image」,若是兩份數據徹底一致就說明沒有髒寫,能夠還原業務數據,若是不一致就說明有髒寫,出現髒寫就須要轉人工處理。
AT 模式的一階段、二階段提交和回滾均由 Seata 框架自動生成,用戶只需編寫「業務 SQL」,便能輕鬆接入分佈式事務,AT 模式是一種對業務無任何侵入的分佈式事務解決方案。
2019 年 3 月份,Seata 開源了 TCC 模式,該模式由螞蟻金服貢獻。TCC 模式須要用戶根據本身的業務場景實現 Try、Confirm 和 Cancel 三個操做;事務發起方在一階段 執行 Try 方式,在二階段提交執行 Confirm 方法,二階段回滾執行 Cancel 方法。
TCC 三個方法描述:
業務模型分 2 階段設計:
用戶接入 TCC ,最重要的是考慮如何將本身的業務模型拆成兩階段來實現。
以「扣錢」場景爲例,在接入 TCC 前,對 A 帳戶的扣錢,只需一條更新帳戶餘額的 SQL 便能完成;可是在接入 TCC 以後,用戶就須要考慮如何將原來一步就能完成的扣錢操做,拆成兩階段,實現成三個方法,而且保證一階段 Try 成功的話 二階段 Confirm 必定能成功。
如上圖所示,
Try 方法做爲一階段準備方法,須要作資源的檢查和預留。在扣錢場景下,Try 要作的事情是就是檢查帳戶餘額是否充足,預留轉帳資金,預留的方式就是凍結 A 帳戶的 轉帳資金。Try 方法執行以後,帳號 A 餘額雖然仍是 100,可是其中 30 元已經被凍結了,不能被其餘事務使用。
二階段 Confirm 方法執行真正的扣錢操做。Confirm 會使用 Try 階段凍結的資金,執行帳號扣款。Confirm 方法執行以後,帳號 A 在一階段中凍結的 30 元已經被扣除,帳號 A 餘額變成 70 元 。
若是二階段是回滾的話,就須要在 Cancel 方法內釋放一階段 Try 凍結的 30 元,使帳號 A 的回到初始狀態,100 元所有可用。
用戶接入 TCC 模式,最重要的事情就是考慮如何將業務模型拆成 2 階段,實現成 TCC 的 3 個方法,而且保證 Try 成功 Confirm 必定能成功。相對於 AT 模式,TCC 模式對業務代碼有必定的侵入性,可是 TCC 模式無 AT 模式的全局行鎖,TCC 性能會比 AT 模式高不少。
Saga 模式是 Seata 即將開源的長事務解決方案,將由螞蟻金服主要貢獻。在 Saga 模式下,分佈式事務內有多個參與者,每個參與者都是一個衝正補償服務,須要用戶根據業務場景實現其正向操做和逆向回滾操做。
分佈式事務執行過程當中,依次執行各參與者的正向操做,若是全部正向操做均執行成功,那麼分佈式事務提交。若是任何一個正向操做執行失敗,那麼分佈式事務會去退回去執行前面各參與者的逆向回滾操做,回滾已提交的參與者,使分佈式事務回到初始狀態。
Saga 模式下分佈式事務一般是由事件驅動的,各個參與者之間是異步執行的,Saga 模式是一種長事務解決方案。
XA 模式是 Seata 將會開源的另外一種無侵入的分佈式事務解決方案,任何實現了 XA 協議的數據庫均可以做爲資源參與到分佈式事務中,目前主流數據庫,例如 MySql、Oracle、DB二、Oceanbase 等均支持 XA 協議。
XA 協議有一系列的指令,分別對應一階段和二階段操做。「xa start」和 「xa end」用於開啓和結束XA 事務;「xa prepare」 用於預提交 XA 事務,對應一階段準備;「xa commit」和「xa rollback」用於提交、回滾 XA 事務,對應二階段提交和回滾。
在 XA 模式下,每個 XA 事務都是一個事務參與者。分佈式事務開啓以後,首先在一階段執行「xa start」、「業務 SQL」、「xa end」和 「xa prepare」 完成 XA 事務的執行和預提交;二階段若是提交的話就執行 「xa commit」,若是是回滾則執行「xa rollback」。這樣便能保證全部 XA 事務都提交或者都回滾。
XA 模式下,用戶只需關注本身的「業務 SQL」,Seata 框架會自動生成一階段、二階段操做;XA 模式的實現以下:
在 XA 模式的一階段,Seata 會攔截「業務 SQL」,在「業務 SQL」以前開啓 XA 事務(「xa start」),而後執行「業務 SQL」,結束 XA 事務「xa end」,最後預提交 XA 事務(「xa prepare」),這樣便完成 「業務 SQL」的準備操做。
執行「xa commit」指令,提交 XA 事務,此時「業務 SQL」纔算真正的提交至數據庫。
執行「xa rollback」指令,回滾 XA 事務,完成「業務 SQL」回滾,釋放數據庫鎖資源。
XA 模式下,用戶只需關注「業務 SQL」,Seata 會自動生成一階段、二階段提交和二階段回滾操做。XA 模式和 AT 模式同樣是一種對業務無侵入性的解決方案;但與 AT 模式不一樣的是,XA 模式將快照數據和行鎖等經過 XA 指令委託給了數據庫來完成,這樣 XA 模式實現更加輕量化。
螞蟻金服從 2007 年開始研發和應用分佈式事務中間件,用 TCC 模式解決各種金融場景的數據一致性問題,後續又演進出 FMT(AT)、XA、Saga 等模式,各類模式分別適用於各種業務場景。咱們決定將螞蟻金服多年的技術積累開源出來,與社區共享螞蟻金服的科技成果。
螞蟻金服內部的分佈式事務產品,在實現原理和使用方式上,與 Seata 相似,不一樣的是,爲了支持雙十一,對性能進行了極致優化,爲了支持金融系統的高可用容災,藉助螞蟻金服三地五中心架構實現了分佈式事務服務的高可用容災;接下來主要介紹螞蟻金服在性能優化和高可用容災方面的實踐經驗。
一般,一個 TM 會產生一筆主事務日誌,一個 RM 會產生一條分支事務日誌,每一個分佈式事務由一個 TM 和若干 RM 組成,一個分佈式事務總共會有 1+N 條事務日誌(N 爲 RM 個數)。
在默認狀況下,分佈式事務執行過程當中客戶端將事務日誌發送給服務端,服務端再將事務日誌存儲至數據庫中,一條事務日誌的存儲鏈路會有 2 次 TCP ,分別是「客戶端到服務端」和「服務端到數據庫」, 咱們稱這種模式爲異庫模式。
在異庫模式下,分佈式事務存儲事務日誌總共須要 2*(1+N) 次左右的 TCP 通訊。在 RM 數量較少的業務場景下,分佈式事務性能還能接收,但有些業務場景下 RM 數量較多,此時事務內 TCP 數量也會增多,分佈式事務性能急劇降低。
在事務執行過程當中,客戶端和服務端進行通訊的目的是爲了存儲事務日誌。若是客戶端在存儲事務日誌時,繞過服務端直接將事務日誌寫入數據庫(如上圖「同庫模式」所示),那麼一筆事務日誌的存儲鏈路就由原來的 2 次 TCP 變成只需訪問一次數據庫即可,每條事務日誌的存儲減小了一次 TCP 通訊,整個分佈式事務就減小了 N+2 次 TCP 請求,分佈式事務的性能大幅提高。咱們將客戶端直接將事務日誌存儲至數據庫的模式稱爲同庫模式。
一般狀況下,分佈式事務發起方會依次執行一階段和二階段方法,而後結束分佈式事務,返回結果。若是讓分佈式事務發起方執行完一階段以後立刻結束並返回結果,二階段交由獨立的線程或者進程異步執行,這樣分佈式事務的二階段會晚幾秒鐘或者若干分鐘執行,但事務的最終結果不會有任何改變。
二階段異步執行以後,分佈式事務的最終結果不會有任何影響,可是事務發起方要執行的內容減小一半(一階段和二階段都執行變成只執行一階段),直觀的用戶感覺是分佈式事務的性能提高了 50%。
爲了保障金融系統的高可用,分佈式事務服務必須達到 99.99% 的可用率。分佈式事務使用了螞蟻金服的三地五中心架構部署,在每一個機房都獨立部署分佈式事務服務,分佈式事務服務是無狀態的,而底層數據庫副本在各機房間也是雙向同步,這樣業務流量從一個機房切到另一個機房,分佈式事務服務不會對業務有任何影響,從而保證了分佈式事務服務的高可用。
在分佈式架構演進中,螞蟻金服對數據庫進了水平拆分,對服務面向功能進行了服務化拆分,從而出現了跨服務、跨數據庫的業務數據一致性挑戰。
2007 年,螞蟻金服自主研發分佈式事務中間件經歷 12 年的嚴苛業務錘鍊。2019 年,將多年的技術積累分享給開源分佈式事務 Seata,並持續投入社區共建。目前 Seata 提供了 AT、TCC、Saga 和 XA 四種模式,每一種模式分別有各自的應用場景,豐富的解決方案幫助用戶解決給了各種場景的數據一致性問題。
最後一部分,分享了螞蟻金服具體的實踐。爲了支持雙十一的高性能需求,對分佈式事務進行了極致的性能優化,例如同庫模式、二階段異步執行。爲了使金融服務的可用性達到 99.99%,螞蟻金服分佈式事務採用三地五中心架構,異地多活的部署模式保障了分佈式事務服務的高可用。
公衆號:金融級分佈式架構(Antfin_SOFA)