1. 程式人生 > >億級流量架構之分散式事務解決方案對比

億級流量架構之分散式事務解決方案對比

上一篇文章( [億級流量架構之分散式事務思路及方法](https://www.cnblogs.com/Courage129/p/14433462.html))中梳理事務到分散式事務的演變過程, 以及分散式事務的處理思路,這篇文章主要從應用的角度對比目前較為流行的一些分散式事務方案,以及一些商業應用。 想讓資料具有高可用性,就得寫多份資料,寫多份資料就會有資料一致性問題,資料已執行問題又會引發效能問題,所以如何權衡,是一件仁者見仁、智者見者的問題,目前的資料一致性,即分散式事務,大概有如下幾種解決方案: 1. Master-Slave 方案。 2. Master-Master 方案。 3. 兩階段和三階段提交方案。 4. Paxos 方案。 第3點在上篇文章中已經講過, 1、2這兒會簡單梳理,重點是第四個方案, 目前很多公司的事務處理方式,例如阿里巴巴的TCC(Try–Confirm–Cancel),亞馬遜的PRC(Plan–Reserve–Confirm)都是兩階段提交的變種, 凡是通過業務補償,或者是在業務層面上做的分散式事務,基本都是兩階段提交的玩法,但是這否是應用層事務處理方式,而在資料層解決事務問題,Paxos是不二之選。 ## Master-Slave 方案 這個也叫主從模式,Slave一般是Master的備份。在這樣的系統中,一般是如下設計的: 1)讀寫請求都由Master負責。 2)寫請求寫到Master上後,由Master同步到Slave上。 從Master同步到Slave上,你可以使用非同步,也可以使用同步,可以使用Master來push,也可以使用Slave來pull。 通常來說是Slave來週期性的pull,所以,是最終一致性。 **這個設計的問題是,如果Master在pull週期內垮掉了,那麼會導致這個時間片內的資料丟失。如果你不想讓資料丟掉,Slave只能成為Read-Only的方式等Master恢復。** 如果可以容忍資料丟掉的話,你可以馬上讓Slave代替Master工作(對於**只負責計算**的結點來說,沒有資料一致性和資料丟失的問題,Master-Slave的方式就可以解決單點問題了) ,Master Slave也可以是強一致性的, 比如:當我們寫Master的時候,Master負責先寫自己,等成功後,再寫Slave,兩者都成功後返回成功,整個過程是同步的,如果寫Slave失敗了,那麼兩種方法,一種是標記Slave不可用報錯並繼續服務(等Slave恢復後同步Master的資料,可以有多個Slave,這樣少一個,還有備份,也就是多個Slave),另一種是回滾自己並返回寫失敗。 **注:一般不先寫Slave,因為如果寫Master自己失敗後,還要回滾Slave,此時如果回滾Slave失敗,就得手工訂正資料** ## Master-Master 方案 Master-Master,主主模式,又叫[Multi-master](http://en.wikipedia.org/wiki/Multi-master_replication),是指一個系統存在兩個或多個Master,每個Master都提供read-write服務。這個模型是Master-Slave的加強版,資料間同步一般是通過Master間的**非同步**完成,所以是最終一致性。 Master-Master的好處是,一臺Master掛了,別的Master可以正常做讀寫服務,他和Master-Slave一樣,當資料沒有被複制到別的Master上時,資料會丟失。很多資料庫都支援Master-Master的Replication的機制。 這種模式的問題在於: 如果多個Master對同一個資料進行修改的時候,這個模型的惡夢就出現了——對資料間的衝突合併,這並不是一件容易的事情。為了解決這問題, Dynamo提出了一種解決辦法, 記錄資料的版本號和修改者, 這也就意味著資料衝突這個事是交給使用者自己搞的。 ## 兩階段和三階段提交方案 這個是業務層分散式事務處理的核心, ,在上篇文章( [億級流量架構之分散式事務思路及方法](https://www.cnblogs.com/Courage129/p/14433462.html))中"二三階段提交協議"介紹得比較詳細了,這兒不多說。需要注意是這是重點,不太瞭解的朋友,為了更好的理解後面的方案, 建議看看相關部分。 ## Paxos 方案 理解Paxos演算法之前,先講一個情景——兩將軍問題,來理解這個演算法是解決了什麼問題。 ### 兩將軍問題 有兩支軍隊,它們分別有一位將軍領導,現在準備攻擊一座修築了防禦工事的城市。這兩支軍隊都駐紮在那座城市的附近,分佔一座山頭。一道山谷把兩座山分隔開來,並且兩位將軍唯一的通訊方式就是派各自的信使來往于山谷兩邊。不幸的是,這個山谷已經被那座城市的保衛者佔領,並且存在一種可能,那就是任何被派出的信使通過山谷是會被捕。 請注意,雖然兩位將軍已經就攻擊那座城市達成共識,但在他們各自佔領山頭陣地之前,並沒有就進攻時間達成共識。兩位將軍必須讓自己的軍隊同時進攻城市才能取得成功。因此,他們必須互相溝通,以確定一個時間來攻擊,並同意就在那時攻擊。如果只有一個將軍進行攻擊,那麼這將是一個災難性的失敗。 這個思維實驗就包括考慮他們如何去做這件事情。下面是我們的思考: 1)第一位將軍先發送一段訊息“讓我們在上午9點開始進攻”。然而,一旦信使被派遣,他是否通過了山谷,第一位將軍就不得而知了。任何一點的不確定性都會使得第一位將軍攻擊猶豫,因為如果第二位將軍不能在同一時刻發動攻擊,那座城市的駐軍就會擊退他的軍隊的進攻,導致他的軍對被摧毀。 2)知道了這一點,第二位將軍就需要傳送一個確認回條:“我收到您的郵件,並會在9點的攻擊。”但是,如果帶著確認訊息的信使被抓怎麼辦?所以第二位將軍會猶豫自己的確認訊息是否能到達。 3)於是,似乎我們還要讓第一位將軍再發送一條確認訊息——“我收到了你的確認”。然而,如果這位信使被抓怎麼辦呢? 4)這樣一來,是不是我們還要第二位將軍傳送一個“確認收到你的確認”的資訊。 靠,於是你會發現,這事情很快就發展成為不管傳送多少個確認訊息,都沒有辦法來保證兩位將軍有足夠的自信自己的信使沒有被敵軍捕獲。 ### Paxos 演算法 Paxos 演算法解決的問題是在一個可能發生上述異常的分散式系統中如何就某個值達成一致,保證不論發生以上任何異常,都不會破壞決議的一致性。一個典型的場景是,在一個分散式資料庫系統中,如果各節點的初始狀態一致,每個節點都執行相同的操作序列,那麼他們最後能得到一個一致的狀態。為保證每個節點執行相同的命令序列,需要在每一條指令上執行一個「一致性演算法」以保證每個節點看到的指令一致。一個通用的一致性演算法可以應用在許多場景中,是分散式計算中的重要問題。從20世紀80年代起對於一致性演算法的研究就沒有停止過。 這個演算法詳細解釋可以參考[維基百科](https://zh.wikipedia.org/wiki/Paxos%E7%AE%97%E6%B3%95)以及raft演算法(Paxos改進)作者的視訊([B站](https://www.bilibili.com/video/BV1wt411y7Da?from=search&seid=10409129153342121411),[YouTube](https://www.youtube.com/watch?v=JEpsBg0AO6o&t=41s)),這兒我用自己的話敘述: 這個演算法將節點分了很多類,官方定義為:Client、Propose、 Acceptor、 Learner,含義不重要,先來理解他們的作用, 看我的文字就行了。 在我們讀書的時候,班級裡有組長,有班委成員,有班務記錄員,下面一一對應起來: 加入我們班決定這周沒去遊玩,現在班會討論去玩什麼專案: Client : 代表普通同學,可以提出方案,需要將方案交給組長(Propose) Propose : 組長,一個班肯定有很多組長,主要任務是將組員的方案提出來給班委投票確認, 同時統計班委們返回的投票結果,並將結果告訴所有人。 Acceptor:班委,這兒的班委具有投票權,但是班委不關心投票的內容,哈哈哈哈,是不是有點奇怪, 先記住,後面就理解了,班委只關心方案是不是已經被提過,只要這個方案之前沒有被提過就會同意,將同意的資訊返回給提案的組長。 當組長收到各個班委回覆的資訊之後,會統計同意的人數,如果人數過半($\frac{N}{2}+1$),那麼就會廣播這個提案已經被認可,這時候Learner會在班務本子上記錄下這個提案。 ![](https://img2020.cnblogs.com/blog/2002319/202102/2002319-20210224202532153-1774749635.png) 如上圖,有一個組員(Client), 一個組長(Proposer), 三個班委(Acceptor), 兩個Learner,當組員提出方案時,組長會將方案提交給三位班委,班委會看看這個方案之前是不是已經提過(主要是根據方案編號,也就是方案編號一致變化,預設是遞增),沒提過的話會通過這個提案,然後組長統計通過的比例,過半數之後會將方案通過的編號進行廣播,班委會回饋資訊,此時Learner會記錄下來同時回饋。 剛剛留下了一個疑惑,為什麼開始階段(Prepare(1))時班委不關心內容呢? 在這幾個角色中,Acceptor(班委)是資料庫(其實也不是資料庫,僅僅是為了方便理解),Learner也是資料庫(備份),當你準備提交一條訊息時,第一步僅僅是看能不能與之建立正常的連線,其次看看這個資料之前是不是已經提交過,如果大部分都可以建立正常的連線並且沒有被提交過,那麼說明我們的資料就可以提交了。 ### Paxos活鎖 前面說過,三個班委(Acceptor)只要接受到的提案是未提交的且過半的話,就會通過,如果一個提案1的組長Proposer正在投票資訊準備通知時,另一個組長Proposer又提交了提案2,那麼班委就會開始討論提案2, 放棄提案1的討論,此時提案1被丟棄,那組長1會將提案重新提交,這導致了死鎖的誕生,為了解決這個問題,可以讓提交方案的組長隨機睡眠一段時間。 ### Paxos改進版 前面例子可以看出是一個兩階段提交的過程, 改進版最主要的點在於,在Acceptor中選出一個主節點,要提交議案直接交給主節點,由主節點將這些訊息同步給其他節點,如果此時過半數,那麼久將資料提交,然後將資訊返回給提議員(組長),什麼意思呢,就是組長提交方案之後,交給班委的頭目,班委頭目統計好投票結果,如果通過了直接通知所有班委以及記錄員,同時將資訊返回給提議員(也就是組長)。 ![](https://img2020.cnblogs.com/blog/2002319/202102/2002319-20210224202544430-1878930003.png) ### 主節點的選舉 對於三個班委(Acceptor)而言,有一個時間週期,如果這個週期內收到主節點的心跳包,那麼就會相安無事,如果週期內沒收到心跳包,那麼就會向其他節點發出請求包,這個包主要是自己要當主節點,請大家投票,所有接受到這個請求包的節點,回覆同意,當一個節點收到的同意資訊過半之後,就會成為主節點,同時廣播這個資訊,收到資訊的節點就成為了從節點。 所有有關操作都會通過這個主節點,主節點再在其餘的節點之間進行投票,通過之後主節點直接提交事務然後將資訊返回給呼叫者。 ### Paxos與資料提交 簡單說來,Paxos的目的是讓整個叢集的結點對某個值的變更達成一致。Paxos演算法基本上來說是個民主選舉的演算法——大多數的決定會成個整個叢集的統一決定。任何一個點都可以提出要修改某個資料的提案,是否通過這個提案取決於這個叢集中是否有超過半數的結點同意(所以Paxos演算法需要叢集中的結點是單數)。 這個演算法有兩個階段(假設這個有三個結點:A,B,C): **第一階段:Prepare階段** A把申請修改的請求Prepare Request發給所有的結點A,B,C。注意,Paxos演算法會有一個Sequence Number(\可以認為是一個提案號,這個數不斷遞增,而且是唯一的,也就是說A和B不可能有相同的提案號),這個提案號會和修改請求一同發出,任何結點在“Prepare階段”時都會拒絕其值小於當前提案號的請求。所以,結點A在向所有結點申請修改請求的時候,需要帶一個提案號,越新的提案,這個提案號就越是是最大的。 如果接收結點收到的提案號n大於其它結點發過來的提案號,這個結點會迴應Yes(本結點上最新的被批准提案號),並保證不接收其它