趙俊java
京東金融mysql
高級Java開發工程師git
一切從ACID開始提及。ACID是本地事務所具備的四大特徵:github
事務做爲總體來執行,要麼所有執行,要麼全不執行。golang
事務應確保數據從一個一致的狀態轉變爲另外一個一致的狀態。sql
多個事務併發執行時,一個事務的執行不該影響其餘事務的執行。數據庫
已提交的事務修改數據會被持久保持。apache
關係型數據庫的本地事務完美的提供了對ACID的原生支持。但在分佈式的場景下,它卻成爲系統性能的桎梏。如何讓數據庫在分佈式場景下知足ACID的特性或找尋相應的替代方案,是本文將要闡述的話題。後端
對於互聯網應用而言,隨着訪問量和數據量的激增,傳統的單體架構模式將沒法知足業務的高速發展。這時,開發者須要把單體應用拆分爲多個獨立的小應用,把單個數據庫按照分片規則拆分爲多個庫和多個表。服務器
數據拆分後,如何在多個數據庫節點間保證本地事務的ACID特性則成爲一個技術難題,而且由此而衍生出了CAP和BASE經典理論。
CAP理論指出,對於分佈式的應用而言,不可能同時知足C(一致性),A(可用性),P(分區容錯性),因爲網絡分區是分佈式應用的基本要素,所以開發者須要在C和A上作出平衡。
因爲C和A互斥性,其權衡的結果就是BASE理論。
對於大部分的分佈式應用而言,只要數據在規定的時間內達到最終一致性便可。咱們能夠把符合傳統的ACID叫作剛性事務,把知足BASE理論的最終一致性事務叫作柔性事務。
一味的追求強一致性,並不是最佳方案。對於分佈式應用來講,剛柔並濟是更加合理的設計方案,即在本地服務中採用強一致事務,在跨系統調用中採用最終一致性。如何權衡系統的性能與一致性,是十分考驗架構師與開發者的設計功力的。
具體到分佈式事務的實現上,業界主要採用了XA協議的強一致規範以及柔性事務的最終一致規範。
XA是X/Open CAE Specification (Distributed Transaction Processing)模型中定義的TM(Transaction Manager)與RM(Resource Manager)之間進行通訊的接口。
Java中的javax.transaction.xa.XAResource定義了XA接口,它依賴數據庫廠商對jdbc-driver的具體實現。
mysql-connector-java-5.1.30的實現可參考:
com.mysql.jdbc.jdbc2.optional.MysqlXAConnection。
在XA規範中,數據庫充當RM角色,應用須要充當TM的角色,即生成全局的txId,調用XAResource接口,把多個本地事務協調爲全局統一的分佈式事務。
一階段提交:弱XA
弱XA經過去掉XA的Prepare階段,以達到減小資源鎖定範圍而提高併發性能的效果。典型的實現爲在一個業務線程中,遍歷全部的數據庫鏈接,依次作commit或者rollback。弱XA同本地事務相比,性能損耗低,但在事務提交的執行過程當中,若出現網絡故障、數據庫宕機等預期以外的異常,將會形成數據不一致,且沒法進行回滾。基於弱XA的事務無需額外的實現成本,所以Sharding-Sphere默認支持。
二階段提交:2PC
二階段提交是XA的標準實現。它將分佈式事務的提交拆分爲2個階段:prepare和commit/rollback。
開啓XA全局事務後,全部子事務會按照本地默認的隔離級別鎖定資源,並記錄undo和redo日誌,而後由TM發起prepare投票,詢問全部的子事務是否能夠進行提交:當全部子事務反饋的結果爲「yes」時,TM再發起commit;若其中任何一個子事務反饋的結果爲「no」,TM則發起rollback;若是在prepare階段的反饋結果爲yes,而commit的過程當中出現宕機等異常時,則在節點服務重啓後,可根據XA recover再次進行commit補償,以保證數據的一致性。
2PC模型中,在prepare階段須要等待全部參與子事務的反饋,所以可能形成數據庫資源鎖定時間過長,不適合併發高以及子事務生命周長較長的業務場景。
Sharding-Sphere支持基於XA的強一致性事務解決方案,能夠經過SPI注入不一樣的第三方組件做爲事務管理器實現XA協議,如Atomikos和Narayana。
柔性事務是對XA協議的妥協和補償,它經過對強一致性要求的下降,已達到下降數據庫資源鎖定時間的效果。柔性事務的種類不少,能夠經過各類不一樣的策略來權衡使用。
一階段提交 + 補償 :最大努力送達(BED)
最大努力送達,是針對於弱XA的一種補償策略。它採用事務表記錄全部的事務操做SQL,若是子事務提交成功,將會刪除事務日誌;若是執行失敗,則會按照配置的重試次數,嘗試再次提交,即最大努力的進行提交,儘可能保證數據的一致性,這裏能夠根據不一樣的業務場景,平衡C和A,採用同步重試或異步重試。
這種策略的優勢是無鎖定資源時間,性能損耗小。缺點是嘗試屢次提交失敗後,沒法回滾,它僅適用於事務最終必定可以成功的業務場景。所以BED是經過事務回滾功能上的妥協,來換取性能的提高。
TCC: Try-Confirm-Cancel
TCC模型是把鎖的粒度徹底交給業務處理,它須要每一個子事務業務都實現Try-Confirm/Cancel接口。
嘗試執行業務;
完成全部業務檢查(一致性);
預留必須業務資源(準隔離性);
確認執行業務;
真正執行業務,不做任何業務檢查;
只使用Try階段預留的業務資源;
Confirm操做知足冪等性;
取消執行業務;
釋放Try階段預留的業務資源;
Cancel操做知足冪等性。
這三個階段都會按本地事務的方式執行,不一樣於XA的prepare,TCC無需將XA的投票期間的全部資源掛起,所以極大的提升了吞吐量。
下面對TCC模式下,A帳戶往B帳戶匯款100元爲例子,對業務的改造進行詳細的分析:
匯款服務和收款服務分別須要實現,Try-Confirm-Cancel接口,並在業務初始化階段將其注入到TCC事務管理器中。
匯款服務
檢查A帳戶有效性,即查看A帳戶的狀態是否爲「轉賬中」或者「凍結」;
檢查A帳戶餘額是否充足;
從A帳戶中扣減100元,並將狀態置爲「轉帳中」;
預留扣減資源,將從A往B帳戶轉帳100元這個事件存入消息或者日誌中;
不作任何操做;
A帳戶增長100元;
從日誌或者消息中,釋放扣減資源。
收款服務
檢查B帳戶帳戶是否有效;
讀取日誌或者消息,B帳戶增長100元;
從日誌或者消息中,釋放扣減資源;
不作任何操做。
由此能夠看出,TCC模型對業務的侵入強,改造的難度大。
消息驅動
消息一致性方案是經過消息中間件保證上下游應用數據操做的一致性。基本思路是將本地操做和發送消息放在一個事務中,下游應用向消息系統訂閱該消息,收到消息後執行相應操做。本質上是依靠消息的重試機制,達到最終一致性。消息驅動的缺點是:耦合度高,須要在業務系統中引入MQ,致使系統複雜度增長。
SAGA
Saga起源於1987年Hector & Kenneth發表的論文Sagas。
參考地址:
https://www.cs.cornell.edu/an...
Saga工做原理
Saga模型把一個分佈式事務拆分爲多個本地事務,每一個本地事務都有相應的執行模塊和補償模塊( TCC中的Confirm和Cancel)。當Saga事務中任意一個本地事務出錯時,能夠經過調用相關的補償方法恢復以前的事務,達到事務最終的一致性。
當每一個Saga子事務 T1, T2, …, Tn 都有對應的補償定義 C1, C2, …, Cn-1,那麼Saga系統能夠保證:
因爲Saga模型中沒有Prepare階段,所以事務間不能保證隔離性,當多個Saga事務操做同一資源時,就會產生更新丟失、髒數據讀取等問題,這時須要在業務層控制併發,例如:
Saga恢復方式
Saga支持向前和向後恢復:
顯然,向前恢復沒有必要提供補償事務,若是你的業務中,子事務(最終)總會成功,或補償事務難以定義或不可能,向前恢復更符合你的需求。理論上補償事務永不失敗,然而,在分佈式世界中,服務器可能會宕機、網絡可能會失敗,甚至數據中心也可能會停電,這時須要提供故障恢復後回退的機制,好比人工干預。
總的來講,TCC和MQ都是以服務爲範圍進行分佈式事務的處理,而XA、BED、SAGA則是以數據庫爲範圍進行分佈式處理,咱們更趨向於選擇後者,對於業務而言侵入小,改造的成本低。
Sharding-Sphere是一套開源的分佈式數據庫中間件解決方案組成的生態圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar這3款相互獨立的產品組成。它們均提供標準化的數據分片、讀寫分離、柔性事務和數據治理功能,可適用於如Java同構、異構語言、容器、雲原生等各類多樣化的應用場景。
項目地址:
https://github.com/sharding-s...
Sharding-Sphere同時支持XA和柔性事務,它容許每次對數據庫的訪問,能夠自由選擇事務類型。分佈式事務對業務操做徹底透明,極大地下降了引入分佈式事務的成本。
Sharding-Sphere事務管理器集成了XA和柔性事務模型:
下面將Sharding-Sphere內部如何用事件驅動方式,將事務從分片主流程中解耦進行詳細說明:
從圖能夠看出在Sharding-core在調用執行引擎時,會根據SQL的種類產生事件進行分發。事務監聽線程在收到符合要求的事件後,再調用對應的事務處理器進行處理。
Sharding-Proxy是基於netty開發的數據庫中間代理層,實現了標準的MySQL協議,能夠看作是一個實現了數據分片的數據庫。Sharding-Proxy已經實現了基於Atomikos的XA事務,爲了保證全部的子事務都處於同一個線程之中,整個Proxy的線程模型進行了以下的調整:
當開啓事務後,Proxy後端的SQL命令執行引擎將採用一通道一線程的模式,此事務線程的生命週期同通道保持一致。事務處理的具體過程與Proxy完全解耦,即Proxy將發佈事務類型的事件,而後Sharding-Sphere-TM根據傳入的事務消息,選擇具體的TM進行處理。
壓測結果代表:XA事務的插入和更新的性能,基本上同跨庫的個數呈線性關係,查詢的性能基本不受影響,建議在併發量不大,每次事務涉及的庫在10個之內時,可使用XA。
Atomikos事務管理器原理分析
Atomikos的事務管理器能夠內嵌到業務進程中,當應用調用TransactionManager.begin時,將會建立本次XA事務,而且與當前線程關聯。同時Atomikos也對DataSource中的connection作了二次封裝,代理connection中含有本次事務相關信息的狀態,而且攔截了connection的JDBC操做。
在createStatement時,調用XAResource.start進行資源註冊;在close時,調用XAResource.end讓XA事務處於idel可提交狀態;在commit或rollback時,依次調用prepare和commit進行二階段提交。
Sharding-Sphere的Saga事務實現
Sharding-Sphere經過與Apache Service Comb的合做,將採用Service Comb的Saga事務引擎做爲的分佈式事務實現。
Apache Service Comb是華爲開源的微服務框架,其中微服務事務處理框架分爲集中式和分佈式協調器。將來會在Sharding-Sphere內部集成Saga集中式協調器,支持同一線程內不一樣服務(本地)間的分佈式事務。
參考連接:
https://github.com/apache/inc...
Service Comb 集中式事務協調器
集中式的協調器,包含了Saga調用請求接收、分析、執行以及結果查詢的內容。任務代理模塊須要預先知道Saga事務調用關係圖,執行模塊根據生成的調用圖產生調用任務,調用相關微服務服務接口。若是服務調用執行出錯,會調用服務的相關的補償方法回滾。
Saga執行模塊經過分析請求的JSON數據,來構建一個調用關係圖。Sharding-Sphere是經過JSON描述Saga事務串行調用子事務或者並行調用子事務。關係調用圖被Saga實現中的任務運行模塊分解成爲一個一個執行任務,執行任務由任務消費者獲取並生成相關的調用 (同時支持串行和並行調用)。Saga任務會根據執行的狀況向Saga Log中記錄對應的Saga事務的關鍵事件,並能夠經過事件查看器查查詢執行狀況。
Sharding-Sphere內嵌Saga事務管理器
Saga以jar包的形式提供分佈式事務治理能力。
對Sharding-Sphere而言,confirm和cancel過程表明了子事務中的正常執行SQL和逆向執行SQL,(將來Sharding-Sphere將提供自動生成逆向SQL的能力)。當啓用Saga柔性事務後,路由完成以後的物理數據源將開啓本地自動提交事務,每次confirm和cancel都會直接提交。
在Sharding-Sphere內部,觸發SQL執行引擎後,將會產生Saga事務事件,這時Sharding-Sphere事務監聽器會註冊本次子事務的confirm和cancel至Saga事務管理器的隊列中;在業務線程觸發commit和rollback後,Saga事務管理器再根據子事務執行的結果,判斷進行confirm重試或者cancel流程。
將來Sharding-Sphere將按照文中介紹的Sharding-Sphere-TM逐步完善整個事務框架:
若是前面的分享太過冗長,那麼千言萬語匯聚成一張表格,歡迎閱讀。
將來,咱們將不斷優化當前的特性,陸續推出你們關注的柔性事務、數據治理等更多新特性。若是有什麼想法、意見和建議,也歡迎留言交流,更歡迎加入到Sharding-Sphere的開源項目中:
Q1:基於XA的事物,能夠應用到微服務架構中嗎?
A1:目前咱們是把事務管理器內嵌到JVM進程中,對於併發量小,短事務的業務,能夠用XA。
Q2:對於各個事務框架開發計劃的前後順序是基本什麼來肯定的呢?
A2:基於難易程度,因此咱們把TCC放到了最後。
Q3:支持多語言嗎?好比golang?
A3:多語言能夠用Sharding-Proxy。
Q4:此次是Proxy實現分佈式事務吧?我記得以前Sharding-JDBC有實現。
A4:此次是整個SS的事務實現,包含Sharding-JDBC和Proxy,目前SJ的實現是弱XA和BED(最大努力送達),之後會增長SAGA和TCC。
Q5:若是我只想用SS裏的事務模塊,能夠嗎?
A5:SS是以事件驅動的方式進行的架構,將來事務模塊只負責事務相關的處理。
Q6:SAGA不支持ACID中的I,我們這邊怎麼考慮的呢?
A6:目前暫不支持隔離性,從此咱們有增長I的規劃,其實全部的柔性事務都不支持I,TCC增長了Try階段,能夠理解是準隔離性,使用SAGA時,能夠在業務層面控制併發,防止髒讀等產生。
Q7:那意思,如今3的版本還不能單獨用事務的模塊?
A7:如今3.0版本,事務模塊依賴了Sharding-JDBC模塊,事務模塊須要監聽Sharding-JDBC和Proxy中的事件,而後進行事務操做。若是你想單獨用事務模塊,須要按Core中定義的事件,在你的業務裏進行發佈。