天天看點

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

分布式事務是什麼

資料庫事務的特性包括原子性(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 通信資料總管。

文章來源:拉勾教育 連結位址:https://kaiwu.lagou.com/course/courseInfo.htm?courseId=69

好了,各位朋友們,本期的内容到此就全部結束啦,能看到這裡的同學都是優秀的同學,下一個升職加薪的就是你了!

如果覺得這篇文章對你有所幫助的話請掃描下面二維碼加個關注。"轉發" 加 "在看",養成好習慣!咱們下期再見!