分佈式事務有哪些解決方案?

來源:http://dwz.date/eaAm


分佈式事務是什麼

數據庫事務的特性包括原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durabilily),簡稱 ACID。

在數據庫執行中,多個併發執行的事務如果涉及到同一份數據的讀寫就容易出現數據不一致的情況,不一致的異常現象有以下幾種。

髒讀,是指一個事務中訪問到了另外一個事務未提交的數據。例如事務 T1 中修改的數據項在尚未提交的情況下被其他事務(T2)讀取到,如果 T1 進行回滾操作,則 T2 剛剛讀取到的數據實際並不存在。

不可重複讀,是指一個事務讀取同一條記錄 2 次,得到的結果不一致。例如事務 T1 第一次讀取數據,接下來 T2 對其中的數據進行了更新或者刪除,並且 Commit 成功。這時候 T1 再次讀取這些數據,那麼會得到 T2 修改後的數據,發現數據已經變更,這樣 T1 在一個事務中的兩次讀取,返回的結果集會不一致。

幻讀,是指一個事務讀取 2 次,得到的記錄條數不一致。例如事務 T1 查詢獲得一個結果集,T2 插入新的數據,T2 Commit 成功後,T1 再次執行同樣的查詢,此時得到的結果集記錄數不同。

髒讀、不可重複讀和幻讀有以下的包含關係,如果發生了髒讀,那麼幻讀和不可重複讀都有可能出現。

不同隔離級別

SQL 標準根據三種不一致的異常現象,將隔離性定義爲四個「隔離級別」(Isolation Level),隔離級別和數據庫的性能呈反比,隔離級別越低,數據庫性能越高;而隔離級別越高,數據庫性能越差,具體如下:

「隔離級別」 「髒讀」 「不可重複讀」 「幻讀」
讀未提交 出現 出現 出現
讀已提交 不出現 出現 出現
可重複讀 不出現 不出現 出現
串行化 不出現 不出現 不出現

(1)Read uncommitted 讀未提交

在該級別下,一個事務對數據修改的過程中,不允許另一個事務對該行數據進行修改,但允許另一個事務對該行數據進行讀,不會出現更新丟失,但會出現髒讀、不可重複讀的情況。

(2)Read committed 讀已提交

在該級別下,未提交的寫事務不允許其他事務訪問該行,不會出現髒讀,但是讀取數據的事務允許其他事務訪問該行數據,因此會出現不可重複讀的情況。

(3)Repeatable read 可重複讀

在該級別下,在同一個事務內的查詢都是和事務開始時刻一致的,保證對同一字段的多次讀取結果都相同,除非數據是被本身事務自己所修改,不會出現同一事務讀到兩次不同數據的情況。因爲沒有約束其他事務的新增Insert操作,所以 SQL 標準中可重複讀級別會出現幻讀。

值得一提的是,可重複讀是 MySQL InnoDB 引擎的默認隔離級別,但是在 MySQL 額外添加了間隙鎖(Gap Lock),可以防止幻讀。

(4)Serializable 序列化

該級別要求所有事務都必須串行執行,可以避免各種併發引起的問題,效率也最低。

對不同隔離級別的解釋,其實是爲了保持數據庫事務中的隔離性(Isolation),目標是使併發事務的執行效果與串行一致,隔離級別的提升帶來的是併發能力的下降,兩者是負相關的關係。


分佈式事務產生的原因

分佈式事務是伴隨着系統拆分出現的,前面我們說過,分佈式系統解決了海量數據服務對擴展性的要求,但是增加了架構上的複雜性,在這一點上,分佈式事務就是典型的體現。

在實際開發中,分佈式事務產生的原因主要來源於存儲和服務的拆分。

存儲層拆分

存儲層拆分,最典型的就是數據庫分庫分表,一般來說,當單表容量達到千萬級,就要考慮數據庫拆分,從單一數據庫變成多個分庫和多個分表。在業務中如果需要進行跨庫或者跨表更新,同時要保證數據的一致性,就產生了分佈式事務問題。在後面的課程中,也會專門來講解數據庫拆分相關的內容。


服務層拆分

服務層拆分也就是業務的服務化,系統架構的演進是從集中式到分佈式,業務功能之間越來越解耦合。

比如電商網站系統,業務初期可能是一個單體工程支撐整套服務,但隨着系統規模進一步變大,參考康威定律,大多數公司都會將核心業務抽取出來,以作爲獨立的服務。商品、訂單、庫存、賬號信息都提供了各自領域的服務,業務邏輯的執行散落在不同的服務器上。

用戶如果在某網站上進行一個下單操作,那麼會同時依賴訂單服務、庫存服務、支付扣款服務,這幾個操作如果有一個失敗,那下單操作也就完不成,這就需要分佈式事務來保證了。


分佈式事務解決方案

分佈式事務的解決方案,典型的有兩階段和三階段提交協議、 TCC 分段提交,和基於消息隊列的最終一致性設計。

2PC 兩階段提交

兩階段提交(2PC,Two-phase Commit Protocol)是非常經典的強一致性、中心化的原子提交協議,在各種事務和一致性的解決方案中,都能看到兩階段提交的應用。

3PC 三階段提交

三階段提交協議(3PC,Three-phase_commit_protocol)是在 2PC 之上擴展的提交協議,主要是爲了解決兩階段提交協議的阻塞問題,從原來的兩個階段擴展爲三個階段,增加了超時機制。

TCC 分段提交

TCC 是一個分佈式事務的處理模型,將事務過程拆分爲 Try、Commit、Cancel 三個步驟,在保證強一致性的同時,最大限度提高系統的可伸縮性與可用性。

兩階段、三階段以及 TCC 協議在後面的課程中我會詳細介紹,接下來介紹幾種系統設計中常用的一致性解決方案。

基於消息補償的最終一致性

異步化在分佈式系統設計中隨處可見,基於消息隊列的最終一致性就是一種異步事務機制,在業務中廣泛應用。

在具體實現上,基於消息補償的一致性主要有本地消息表和第三方可靠消息隊列等。

下面介紹一下本地消息表,本地消息表的方案最初是由 ebay 的工程師提出,核心思想是將分佈式事務拆分成本地事務進行處理,通過消息日誌的方式來異步執行。

本地消息表是一種業務耦合的設計,消息生產方需要額外建一個事務消息表,並記錄消息發送狀態,消息消費方需要處理這個消息,並完成自己的業務邏輯,另外會有一個異步機制來定期掃描未完成的消息,確保最終一致性。

下面我們用下單減庫存業務來簡單模擬本地消息表的實現過程:


(1)系統收到下單請求,將訂單業務數據存入到訂單庫中,並且同時存儲該訂單對應的消息數據,比如購買商品的 ID 和數量,消息數據與訂單庫爲同一庫,更新訂單和存儲消息爲一個本地事務,要麼都成功,要麼都失敗。

(2)庫存服務通過消息中間件收到庫存更新消息,調用庫存服務進行業務操作,同時返回業務處理結果。

(3)消息生產方,也就是訂單服務收到處理結果後,將本地消息表的數據刪除或者設置爲已完成。

(4)設置異步任務,定時去掃描本地消息表,發現有未完成的任務則重試,保證最終一致性。

以上就是基於本地消息表一致性的主流程,在具體實踐中,還有許多分支情況,比如消息發送失敗、下游業務方處理失敗等,感興趣的同學可以思考下。


不要求最終一致性的柔性事務

除了上述幾種,還有一種不保證最終一致性的柔性事務,也稱爲盡最大努力通知,這種方式適合可以接受部分不一致的業務場景。

分佈式事務有哪些開源組件

分佈式事務開源組件應用比較廣泛的是螞蟻金服開源的 Seata,也就是 Fescar,前身是阿里中間件團隊發佈的 TXC(Taobao Transaction Constructor)和升級後的 GTS(Global Transaction Service)。

Seata 的設計思想是把一個分佈式事務拆分成一個包含了若干分支事務(Branch Transaction)的全局事務(Global Transaction)。分支事務本身就是一個滿足 ACID 的 本地事務,全局事務的職責是協調其下管轄的分支事務達成一致,要麼一起成功提交,要麼一起失敗回滾。

在 Seata 中,全局事務對分支事務的協調基於兩階段提交協議,類似數據庫中的 XA 規範,XA 規範定義了三個組件來協調分佈式事務,分別是 AP 應用程序、TM 事務管理器、RM 資源管理器、CRM 通信資源管理器。



以上,希望對你有所幫助!



End




乾貨分享



這裏爲大家準備了一份小小的禮物,關注公衆號,輸入如下代碼,即可獲得百度網盤地址,無套路領取!

001:《程序員必讀書籍》
002:《從無到有搭建中小型互聯網公司後臺服務架構與運維架構》
003:《互聯網企業高併發解決方案》
004:《互聯網架構教學視頻》
006:《SpringBoot實現點餐系統》
007:《SpringSecurity實戰視頻》
008:《Hadoop實戰教學視頻》
009:《騰訊2019Techo開發者大會PPT》

010: 微信交流羣


我就知道你“在看”






本文分享自微信公衆號 - JAVA日知錄(javadaily)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章