微信公衆號「後端進階」,專一後端技術分享:Java、Golang、WEB框架、分佈式中間件、服務治理等等。sql
在微服務架構體系下,咱們能夠按照業務模塊分層設計,單獨部署,減輕了服務部署壓力,也解耦了業務的耦合,避免了應用逐漸變成一個龐然怪物,從而能夠輕鬆擴展,在某些服務出現故障時也不會影響其它服務的正常運行。總之,微服務在業務的高速發展中帶給咱們愈來愈多的優點,可是微服務並非十全十美,所以不能盲目過分濫用,它有不少不足,並且會給系統帶來必定的複雜度,其中伴隨而來的分佈式事務問題,是微服務架構體系下必然須要處理的一個痛點,也是業界一直關注的一個領域,所以也出現了諸如 CAP 和 BASE 等理論。數據庫
在今年年初,阿里開源了一個分佈式事務中間件,起初起名爲 Fescar,後更名爲 Seata,在它開源之初,我就知道它確定要火,由於這是一個解決痛點的開源項目,Seata 一開始就是衝着對業務無侵入與高性能方向走,這正是咱們對解決分佈式事務問題迫切的需求。由於待過的幾家公司,用的都是微服務架構,可是在解決分佈式事務的問題上都不太優雅,因此我也在一直關注 Seata 的發展,今天就簡要說說它的一些設計上的原理,後續我將會對它的各個模塊進行深刻源碼分析,感興趣的能夠持續關注個人公衆號或者博客,不要跟丟。後端
目前分佈式事務解決的方案主要有對業務無入侵和有入侵的方案,無入侵方案主要有基於數據庫 XA 協議的兩段式提交(2PC)方案,它的優勢是對業務代碼無入侵,可是它的缺點也是很明顯:必需要求數據庫對 XA 協議的支持,且因爲 XA 協議自身的特色,它會形成事務資源長時間得不到釋放,鎖定週期長,並且在應用層上面沒法干預,所以它性能不好,它的存在至關於七傷拳那樣「傷人七分,損己三分」,所以在互聯網項目中並非很流行這種解決方案。微信
爲了這個彌補這種方案帶來性能低的問題,大佬們又想出了不少種方案來解決,但這無一例外都須要經過在應用層作手腳,即入侵業務的方式,好比很出名的 TCC 方案,基於 TCC 也有不少成熟的框架,如 ByteTCC、tcc-transaction 等。以及基於可靠消息的最終一致性來實現,如 RocketMQ 的事務消息。架構
入侵代碼的方案是基於現有情形「無可奈何」才推出的解決方案,實際上它們實現起來很是不優雅,一個事務的調用一般伴隨而來的是對該事務接口增長一系列的反向操做,好比 TCC 三段式提交,提交邏輯必然伴隨着回滾的邏輯,這樣的代碼會使得項目很是臃腫,維護成本高。框架
針對上面所說的分佈式事務解決方案的痛點,那很顯然,咱們理想的分佈式事務解決方案確定是性能要好並且要對業務無入侵,業務層上無需關心分佈式事務機制的約束,Seata 正是往這個方向發展的,所以它很是值得期待,它將給咱們的微服務架構帶來質的提高。異步
那 Seata 是怎麼作到的呢?下面說說它的各個模塊之間的關係。分佈式
Seata 的設計思路是將一個分佈式事務能夠理解成一個全局事務,下面掛了若干個分支事務,而一個分支事務是一個知足 ACID 的本地事務,所以咱們能夠操做分佈式事務像操做本地事務同樣。微服務
Seata 內部定義了 3個模塊來處理全局事務和分支事務的關係和處理過程,這三個組件分別是:源碼分析
簡要說說整個全局事務的執行步驟:
Seata 的事務提交方式跟 XA 協議的兩段式提交在整體上來講基本是一致的,那它們之間有什麼不一樣呢?
咱們都知道 XA 協議它依賴的是數據庫層面來保障事務的一致性,也便是說 XA 的各個分支事務是在數據庫層面上驅動的,因爲 XA 的各個分支事務須要有 XA 的驅動程序,一方面會致使數據庫與 XA 驅動耦合,另外一方面它會致使各個分支的事務資源鎖定週期長,這也是它沒有在互聯網公司流行的重要因素。
基於 XA 協議以上的問題,Seata 另闢蹊徑,既然在依賴數據庫層會致使這麼多問題,那我就從應用層作手腳,這還得從 Seata 的 RM 模塊提及,前面也說過 RM 的主要做用了,其實 RM 在內部作了對數據庫操做的代理層,以下:
Seata 在數據源作了一層代理層,因此咱們使用 Seata 時,咱們使用的數據源實際上用的是 Seata 自帶的數據源代理 DataSourceProxy,Seata 在這層代理中加入了不少邏輯,主要是解析 SQL,把業務數據在更新先後的數據鏡像組織成回滾日誌,並將 undo log 日誌插入 undo_log 表中,保證每條更新數據的業務 sql 都有對應的回滾日誌存在。
這樣作的好處就是,本地事務執行完能夠當即釋放本地事務鎖定的資源,而後向 TC 上報分支狀態。當 TM 決議全局提交時,就不須要同步協調處理了,TC 會異步調度各個 RM 分支事務刪除對應的 undo log 日誌便可,這個步驟很是快速地能夠完成;當 TM 決議全局回滾時,RM 收到 TC 發送的回滾請求,RM 經過 XID 找到對應的 undo log 回滾日誌,而後執行回滾日誌完成回滾操做。
如上圖所示,XA 方案的 RM 是放在數據庫層的,它依賴了數據庫的 XA 驅動程序。
如上圖所示,Seata 的 RM 其實是已中間件的形式放在應用層,不用依賴數據庫對協議的支持,徹底剝離了分佈式事務方案對數據庫在協議支持上的要求。
下面詳細說說分支事務是如何提交和回滾的:
分支事務利用 RM 模塊中對 JDBC 數據源代理,加入了若干流程,對業務 SQL 進行解釋,把業務數據在更新先後的數據鏡像組織成回滾日誌,並生成 undo log 日誌,對全局事務鎖的檢查以及分支事務的註冊等,利用本地事務 ACID 特性,將業務 SQL 和 undo log 寫入同一個事物中一同提交到數據庫中,保證業務 SQL 一定存在相應的回滾日誌,最後對分支事務狀態向 TC 進行上報。
TM決議全局提交:
當 TM 決議提交時,就不須要同步協調處理了,TC 會異步調度各個 RM 分支事務刪除對應的 undo log 日誌便可,這個步驟很是快速地能夠完成。這個機制對於性能提高很是關鍵,咱們知道正常的業務運行過程當中,事務執行的成功率是很是高的,所以能夠直接在本地事務中提交,這步對於提高性能很是顯著。
TM決議全局回滾:
當 TM 決議回滾時,RM 收到 TC 發送的回滾請求,RM 經過 XID 找到對應的 undo log 回滾日誌,而後利用本地事務 ACID 特性,執行回滾日誌完成回滾操做並刪除 undo log 日誌,最後向 TC 進行回滾結果上報。
業務對以上全部的流程都無感知,業務徹底不關心全局事務的具體提交和回滾,並且最重要的一點是 Seata 將兩段式提交的同步協調分解到各個分支事務中了,分支事務與普通的本地事務無任何差別,這意味着咱們使用 Seata 後,分佈式事務就像使用本地事務同樣,徹底將數據庫層的事務協調機制交給了中間件層 Seata 去作了,這樣雖然事務協調搬到應用層了,可是依然能夠作到對業務的零侵入,從而剝離了分佈式事務方案對數據庫在協議支持上的要求,且 Seata 在分支事務完成以後直接釋放資源,極大減小了分支事務對資源的鎖定時間,完美避免了 XA 協議須要同步協調致使資源鎖定時間過長的問題。
上面說的實際上是 Seata 的默認模式,也叫 AT 模式,它是相似於 XA 方案的兩段式提交方案,而且是對業務無侵入,可是這種機制依然是須要依賴數據庫本地事務的 ACID 特性,有沒有發現,我在上面的圖中都強調了必須是支持 ACID 特性的關係型數據庫,那麼問題就來了,非關係型或者不支持 ACID 的數據庫就沒法使用 Seata 了,別慌,Seata 現階段爲咱們準備了另一種模式,叫 MT 模式,它是一種對業務有入侵的方案,提交回滾等操做須要咱們自行定義,業務邏輯須要被分解爲 Prepare/Commit/Rollback 3 部分,造成一個 MT 分支,加入全局事務,它存在的意義是爲 Seata 觸達更多的場景。
只不過,它不是 Seata 「主打」的模式,它的存在僅僅做爲補充的方案,從以上官方的發展遠景就能夠看出來,Seata 的目標是始終是對業務無入侵的方案。
注:本文圖片設計參考Seata官方圖