天天看點

不好意思,懂分布式事務的你真的很了不起,上篇

分布式事務這個話題,我相信對于身在網際網路中的開發者們一定都不陌生。電商系統最容易出現分布式事務的處理,

拿使用者在電商平台購買一個商品來切入今天的主題,使用者首先下單,然後平台要扣減庫存。建立訂單方面的工作和庫存的扣減工作一般都不在同一台機器上。而使用者購買到商品的行為,必須要下單和扣減庫存都成功,才算這次的交易成功,反之則失敗。

01

分布式事務是什麼?

作為開發的我們,語言不限,無論java還是php,肯定知道事務是什麼,尤其是參與資料庫比如MySql方面的開發,應該最能了解事務。

事務(Transaction),一般是指要做的或所做的事情,由事務開始(begin transaction)和事務結束(end transaction)之間執行的全體操作組成,簡單的講就是,要麼全部被執行,要麼就全部失敗。

那分布式事務,自然就是運作在分布式系統中的事務,是由多個不同的機器上的事務組合而成的。同上,隻有分布式系統中所有事務執行了才能是成功,否則失敗。

我們了解了分布式事務的基本含義,那想要深入了解分布式事務,就得先要搞清楚事務的基本特征ACID。

  • 原子性(Atomicity),一個事務是一個不可分割的工作機關,事務中包括的諸操作要麼都做,要麼都不做。
  • 一緻性(Consistency),指事務執行前和執行後,資料是完整的。舉個例子,我們兩個人各有1000元,對于銀行來說總共是2000元。

現在我給你轉500元,這裡就會涉及到兩個步驟操作,一是我的賬戶減了500元變成了500元,另一個是你的賬戶裡增加了500元變成了1500元,

總共還是2000元,這就是保證了一緻性。但是不允許發生我的減了500元而你的沒增加還是1000元,最後這個總數就變成了1500元,是以這就造成了不一緻。

  • 隔離性(Isolation),一個事務的執行不能被其他事務幹擾。即一個事務内部的操作及使用的資料對并發的其他事務是隔離的,并發執行的各個事務之間不能互相幹擾。
  • 持久性(Durability),也稱為永久性,一個事務一旦送出,它對資料庫中資料的改變就應該是永久性的儲存下來了。

02

如何實作分布式事務

首先,我們想一下分布式事務是為了解決分布式系統的什麼問題?通過上面事務特征的介紹後,其實我們已經知道了,它就是為了解決分布式環境下,多個獨立事務一緻性問題。現在業界内常用的有3種方案實作分布式事務:

  • 二階段送出協定形式,二階段是基于XA協定的,采取強一緻性,遵從ACID
  • 三階段送出協定形式,采取強一緻性,遵從ACID
  • 基于消息的最終一緻性形式,采取最終一緻性,遵從BASE理論

注:我想講這三個方案講的清晰點,是以我将這次分布式事務專題分為上下兩篇來說,上篇主要講二階段和三階段送出,下篇講最終一緻性方案。這樣是為了節約大家寶貴時間并且不會造成學習疲勞。

二階段送出形式

二階段送出協定是協調所有分布式原子事務參與者,并決定送出或取消(復原)的分布式算法。

二階段送出中,系統内有兩個角色,

  • 協調者
  • 參與者

協調者(老大),隻有一個,由它來執行送出和復原操作;參與者(小弟們),一般有多個,通常是由資料庫來實作。

為了保證資料的一緻性,是以我們需要有一個協調者(這裡的算法就是我前面分享那篇分布式選主算法集中式,忘記的可以再去複習下)來管理所有的節點,來保證各個事務的正确送出,若是有送出失敗的則放棄。下面我們來詳細看看送出的過程。

二階段送出過程

既然是二階段,那也就是說它有兩個執行步驟:

  • 投票(vote)
  • 送出(commit)

投票階段,協調者會像所有參與者發送能送出(canCommit)的請求,然後,參與者收到請求後,會各自在本地執行自己的事務操作并記錄行為,但自己并不真正送出,成功就會發送同意Yes指令給協調者,失敗則發送終止No指令。

當協調者收到了所有參與者傳回的指令yes或者no,則會進入真正送出階段,這時候就會給協調者發送doCommit或者doAbort消息。

協調者收到參與者的全是yes消息,則會像參與者發送doCommit消息指令,此時參與者就會完成後續的操作真正的送出事務以及釋放連接配接資源等,接着就會向協調者發送haveCommittd已經送出了的消息。

協調者收到的所有參與者中有no指令,那麼之前yes的參與者會根據自己的日志進行復原操作,然後就向協調者發送haveCommitted消息。

協調者收到了所有參與者的已經送出haveCommitted消息,則表明了整個分布式事務的結束。

好了,二階段送出實作分布式事務,大家應該都很明白了,下面我覺得還是需要結合一個案例來說明一下這個過程幫助你更好的了解,真正做到心裡不虛。

03

案例分析

我在我們平台商城裡面購買一台Android手機,會涉及到我們的訂單系統、庫存系統兩個主系統的協作。那麼,我這樣一個購買Android手機的行為怎麼展現二階段送出實作的分布式事務呢?

第一階段,協調者會像參與者訂單系統以及庫存系統發送canCommit消息,然後訂單系統就會進行訂單的相關操作,如鎖住訂單庫進行新增訂單的操作,完成後就發送一個Yes消息給協調者;庫存系統發現現在我的這款手機庫存已經為0了,就會終止扣減庫存方面的操作,發送No消息給協調者。

不好意思,懂分布式事務的你真的很了不起,上篇

第二階段,協調者收到了庫存系統失敗的消息,則會給訂單以及庫存系統發送取消送出事務的消息。

此時,訂單系統收到老大哥的取消送出事務的指令,就會将之前的操作進行復原操作;然後訂單系統和庫存系統向老大協調者傳回完成事務送出的消息HaveCommited。最後,本次事務完成。

不好意思,懂分布式事務的你真的很了不起,上篇

二階段送出基本上滿足了ACID特征,且實作的是資料的強一緻性。但是它也是有缺點的,看過我前面的架構文章,應該很容易猜出了其中一個缺點吧,那就是單點故障,因為協調者隻有一個(不能有多個老大的呀,可以複習下前面分布式選主算法哈),那還有有沒有其他的缺點呢?肯定是有的啊:

  • 單點故障:事務的發起、送出還是取消,均是由老大協調者管理的,隻要老大當機,那就涼涼了。
  • 同步阻塞缺點:從上面介紹以及例子可看出,我們的參與系統中在沒收到老大的真正送出還是取消事務指令的時候,就是鎖定目前的資源,并不真正的做些事務相關操作,是以,整個分布式系統環境就是阻塞的。
  • 資料不一緻缺點:就是說在老大協調者像小弟們發送真正送出事務的時候,部分網路故障,造成部分系統沒收到真正的指令,那麼就會出現部分送出部分沒送出,是以,這就會導緻資料的不一緻。

04

三階段送出

上面我們清楚了二階段送出,也明白了它有哪些缺點。正是因為有些缺點,是以,就有了優化方案,那就是三階段送出。那麼,三階段送出做出了哪些優化呢?

加入逾時機制,其實架構思想都是想通的,我前面分享的高可用就也有逾時機制。

加入預送出階段,在第一階段和第二階段之間增加了一個預送出的階段,其實我們就了解為中間緩沖的狀态好明白了。

是以,現在三階段送出就是有這三個主階段,canCommit、preCommit、doCommit這三個階段,下面我們來分析下:

第一階段,canCommit

這第一階段和上面二階段送出中的第一階段是類似的,那這個階段中在分布式系統中事務的流程是怎樣的?看下圖:

不好意思,懂分布式事務的你真的很了不起,上篇

第二階段,preCommit

當所有參與者向老大協調者發送Yes消息的時候,協調者就會發送preCommit指令給他們,告訴他們可以準備送出了,參與者就會做一些本地事務的真正業務操作,但是不真的送出,自己做完了就傳回ACK确認消息回去。

當參與者有傳回No消息的時候,老大協調者就會告訴小弟們Abort消息,小弟們收到了老大的中斷事務送出的指令就會停止真正的事務送出動作。

不好意思,懂分布式事務的你真的很了不起,上篇

第三階段,doCommit

到了這個階段就是真正的事務送出部分了,然後宣告真個分布式事務結束。

不好意思,懂分布式事務的你真的很了不起,上篇

總結,今天我們學習到了分布式事務是什麼,并且掌握了分布式事務實作的兩種方式,二階段送出以及三階段送出。這兩個方案都是為了實作資料的強一緻性的。還有第三種方案基于消息的最終資料一緻性,由于時間原因,我們放在下一篇進行講述。

下一集預告:分布式事務最終資料一緻性方案。

關于架構師修煉

本号旨在分享一線網際網路各種技術架構解決方案,分布式以及高并發等相關專題,同時會将作者的學習總結進行整理并分享。

更多技術專題,敬請期待