框架篇:分布式一致性解決方案
本文轉(zhuǎn)載自微信公眾號「潛行前行」,作者cscw。轉(zhuǎn)載本文請聯(lián)系潛行前行公眾號。
前言
上一篇架構(gòu)篇:分布式理論CAP、BASE[1],我們了解到分布式存在的問題以及大致的解決理論,但是具體的實現(xiàn)協(xié)議或者方案有哪些?
- 分布式一致性
- 分布式共識算法
- paoxs、Raft、zab
- 分布式事務(wù)一致性
- 分布式事務(wù)一致性的實現(xiàn)方案(XA模式和AT模式)
- 兩階段提交
- 三階段提交
- 柔性事務(wù)TCC
- AT模式
- 事件通知
1 分布式一致性
什么是分布式一致性?分布式一致性其實更多是偏向解決多個服務(wù)間的數(shù)據(jù)副本狀態(tài)的一致,而不同于關(guān)系型數(shù)據(jù)庫的一致性(數(shù)據(jù)的約束)
2 分布式共識算法
paoxs算法
- Paxos算法是基于消息傳遞且具有高度容錯特性的一致性算法,是目前公認的解決分布式一致性問題最有效的算法之一
- Paxos算法的通俗理解
- 假設(shè)有十個人要去旅游,目的地有成都和拉薩兩個地點。為了統(tǒng)一目的地,簡單的方法可以拉個微信群組聊天,大家投票,按少數(shù)服從多數(shù)的原則。但是在Paxos算法里,覺得微信平臺不可靠,它掛了怎么辦?Paxos的原則是容錯性一定要很強,所以paxos采取相互發(fā)短信
- 找另外三個人當(dāng)中介人(也可從十個人中選,也不局限三個中介),十個人給他們發(fā)短信,中介者之間可以不通信
- 「申請階段」:每個人的短信都會帶一個發(fā)送時間,中介只會和最新短信的提議者交流,而且只能和一個人交流。每個人瘋狂向中介發(fā)短信,希望獲得溝通權(quán)
- 「溝通階段」:如果獲得半數(shù)的中介者溝通權(quán)。提議者則會給這些中介提議自己希望的旅游地(例如成都)。而收到的結(jié)果有三種;
- A: 超過半數(shù)的中介者同意,收東西去成都;
- B: 至少有一個中介者決定了旅游地(不一定是成都,可能是其他提議者和中介商定的拉薩),那先看看是否超過半數(shù)的旅游地,如果沒有,則下次頂最近時間選擇出的旅游地
- C: 失去溝通權(quán),再繼續(xù)發(fā)短信。。。。。。
- Paxos的一致性,是為了解決冗余副本的一致性,和關(guān)系型數(shù)據(jù)庫中ACID的一致性說的不是一個東西
Raft算法
- 由于Paxos難以理解,也難以實現(xiàn)。于是有了新的共識算法。Raft有三種角色
- Leader: 處理所有客戶端交互,日志復(fù)制等,同一時刻只有一個有效的Leader
- Follower: 類似選民,完全被動
- Candidate候選人: 可以被選為一個新的領(lǐng)導(dǎo)人
選舉階段
一開始任何一個服務(wù)器都是Follwer,它們內(nèi)置一個倒計時,當(dāng)?shù)褂嫊r結(jié)束時變成Candidate,向其他follwers發(fā)出要求選舉自己的請求
此時有三個狀態(tài)
A:超過半數(shù)follwers追隨,成為新的leader
B:存在競爭者,且有超過半數(shù)追隨者,放棄競選,成為其follwer
C:存在競爭者,大家半斤八兩。Candidate則在下個競選周期term再次發(fā)起競選,此時也有內(nèi)置一個倒計時,誰先倒計時結(jié)束快,誰則先成為搶占半數(shù)follwer的leader(注意:前一輪成為別人的follwer不能在競選了)
日志復(fù)制階段
1:Leader領(lǐng)導(dǎo)人已經(jīng)選出,客戶端發(fā)出增加一個日志的要求,比如日志是"hello"
2:Leader要求Followe遵從他的指令,都將這個新的日志內(nèi)容追加到他們各自日志中
3:大多數(shù)follower服務(wù)器將日志寫入磁盤文件后,確認追加成功,發(fā)出Commited Ok
4:在下一個心跳heartbeat中,Leader會通知所有Follwer更新commited 項目
如果在這一過程中,發(fā)生了網(wǎng)絡(luò)分區(qū)或者網(wǎng)絡(luò)通信故障。使得Leader不能訪問大多數(shù)Follwers了,而follwers重新選舉新的Leade對外提供服務(wù)。在恢復(fù)網(wǎng)絡(luò)時,舊的leader會成為擁有多數(shù)follwer的新Leader的follwer。故障期間的commit回滾
zab算法
ZXID
協(xié)議的事務(wù)編號 Zxid 設(shè)計中, Zxid 是一個 64位的數(shù)字
其中低 32 位是一個簡單的單調(diào)遞增的計數(shù)器, 針對客戶端每一個事務(wù)請求,計數(shù)器加 1
而高 32 位則代表 Leader 周期 epoch 的編號,每個當(dāng)選產(chǎn)生一個新的 Leader 服務(wù)器,就會從這個 Leader 服務(wù)器上取出其本地日志中的最大事務(wù) ZXID ,并從中讀取 epoch 值,然后加 1 ,以此作為新的 epoch。而低 32 位計數(shù)器則從 0 開始重新計數(shù)
崩潰恢復(fù)模式(選舉)
集群初始化或者Leader失去連接時,節(jié)點(任意節(jié)點)發(fā)起選主,然后集群其他節(jié)點會為發(fā)起選主的節(jié)點進行投票
節(jié)點B判斷確定A可以成為Leader,那么節(jié)點B就投票給節(jié)點A,判斷的依據(jù)是:election epoch(A) > election epoch (B) || zxid(A) > zxid(B) || sid(A) > sid(B)。并更新自己的投票為B投票
sid是服務(wù)ID,人為配置的
消息廣播模式
- Leader將客戶端的request轉(zhuǎn)化成一個Proposal(提議)
- Leader為每一個Follower準(zhǔn)備了一個FIFO隊列,并把Proposal發(fā)送到隊列上
- Leader若收到follower的半數(shù)以上ACK反饋
- Leader向所有的follower發(fā)送commit
一些細節(jié)
Leader在收到客戶端請求之后,會將這個請求封裝成一個事務(wù),并給這個事務(wù)分配一個全局遞增的唯一ID,稱為事務(wù)ID(ZXID),ZAB協(xié)議需要保證事務(wù)的順序,因此必須將每一個事務(wù)按照ZXID進行先后排序然后處理
在Leader和Follwer之間還有一個消息隊列,用來解耦他們之間的耦合,解除同步阻塞
zookeeper集群中為保證任何所有進程能夠有序的順序執(zhí)行,只能是 Leader 服務(wù)器接受寫請求,即使是 Follower 服務(wù)器接受到客戶端的請求,也會轉(zhuǎn)發(fā)到 Leader 服務(wù)器進行處理
3 分布式事務(wù)一致性
對于分布式一致性和分布式事務(wù)一致性。我更愿意區(qū)分開來:
A-分布式一致性是為了解決數(shù)據(jù)分布在多個服務(wù)的狀態(tài)一致(多個副本保持一致)
B-分布式事務(wù)一致性,更加類似關(guān)系型數(shù)據(jù)庫的一致性,是約束數(shù)據(jù)在分布式服務(wù)的關(guān)系(比如數(shù)據(jù)a在服務(wù)A的狀態(tài)和數(shù)據(jù)b在服務(wù)B需要保持一個固定的映射關(guān)系)
分布式共識算法和分布式一致性的區(qū)別
共識算法就是為了解決分布式一致性的算法,但不適合解決分布式事務(wù)一致性(可以解決只是不合適)
4 分布式事務(wù)一致性的實現(xiàn)方案(XA模式和AT模式)
XA模式是預(yù)提交數(shù)據(jù)模式(預(yù)提交數(shù)據(jù)無法被其他事務(wù)訪問),如果發(fā)生故障,則回滾預(yù)提交的數(shù)據(jù)
AT模式的數(shù)據(jù)是確認提交的,只不過存在鎖,使該數(shù)據(jù)無法被其他事務(wù)訪問。如果發(fā)生故障,則使用沖正操作修復(fù)數(shù)據(jù)。相對XA模式,AT模式更適合解決分布式事務(wù),減少阻塞等待時間
兩階段提交(強一致性)(XA模式)
二階段提交協(xié)議(Two-phase Commit,即 2PC)是常用的分布式事務(wù)解決方案,即將事務(wù)的提交過程分為兩個階段來進行處理:準(zhǔn)備階段和提交階段
處理流程
階段 1:準(zhǔn)備階段
協(xié)調(diào)者向所有參與者發(fā)送事務(wù)內(nèi)容,詢問是否可以提交事務(wù),并等待所有參與者答復(fù)。
各參與者執(zhí)行事務(wù)操作,將 undo 和 redo 信息記入事務(wù)日志中(但不提交事務(wù))。
如參與者執(zhí)行成功,給協(xié)調(diào)者反饋 yes,即可以提交;如執(zhí)行失敗,給協(xié)調(diào)者反饋 no,即不可提交
階段 2:提交階段
如果協(xié)調(diào)者收到了參與者的失敗消息或者超時,直接給每個參與者發(fā)送回滾(rollback)消息;否則,發(fā)送提交(commit)消息。
參與者根據(jù)協(xié)調(diào)者的指令執(zhí)行提交或者回滾操作,釋放所有事務(wù)處理過程中使用的鎖資源
2PC 方案缺點:
性能問題:所有參與者在事務(wù)提交階段處于同步阻塞狀態(tài),占用系統(tǒng)資源,容易導(dǎo)致性能瓶頸
可靠性問題:如果協(xié)調(diào)者存在單點故障問題,如果協(xié)調(diào)者出現(xiàn)故障,參與者將一直處于鎖定狀態(tài)
數(shù)據(jù)一致性問題:在提交階段commit時,如果發(fā)生局部網(wǎng)絡(luò)問題,一部分事務(wù)參與者收到了提交消息,另一部分事務(wù)參與者沒收到提交消息,會導(dǎo)致了節(jié)點之間數(shù)據(jù)的不一致
三階段提交(強一致性)(XA模式)
三階段提交協(xié)議,是二階段提交協(xié)議的改進版本,與二階段提交不同的是,引入超時機制。同時在協(xié)調(diào)者和參與者中都引入超時機制
處理流程
階段 1:canCommit
協(xié)調(diào)者向參與者發(fā)送 commit 請求,參與者如果可以提交就返回 yes 響應(yīng)(參與者不執(zhí)行事務(wù)操作),否則返回 no 響應(yīng):
協(xié)調(diào)者向所有參與者發(fā)出包含事務(wù)內(nèi)容的 canCommit 請求,詢問是否可以提交事務(wù),并等待所有參與者答復(fù)
參與者收到 canCommit 請求后,如果認為可以執(zhí)行事務(wù)操作,則反饋 yes 并進入預(yù)備狀態(tài),否則反饋 no
階段 2:preCommit
- 協(xié)調(diào)者根據(jù)階段 1 canCommit 參與者的反應(yīng)情況來決定是否可以進行基于事務(wù)的 preCommit 操作。根據(jù)響應(yīng)情況,有以下兩種可能
- 「情況 1」:階段 1 所有參與者均反饋 yes,參與者預(yù)執(zhí)行事務(wù)
- 協(xié)調(diào)者向所有參與者發(fā)出 preCommit 請求,進入準(zhǔn)備階段
- 參與者收到 preCommit 請求后,執(zhí)行事務(wù)操作,將 undo 和 redo 信息記入事務(wù)日志中(但不提交事務(wù))
- 各參與者向協(xié)調(diào)者反饋 ack 響應(yīng)或 no 響應(yīng),并等待最終指令
- 「情況 2」:階段 1 任何一個參與者反饋 no,「或者等待協(xié)調(diào)者超時,無法收到所有參與者的反饋,即中斷事務(wù)」
- 協(xié)調(diào)者向所有參與者發(fā)出 abort 請求
- 「無論收到協(xié)調(diào)者發(fā)出的 abort 請求,或者在等待協(xié)調(diào)者請求過程中出現(xiàn)超時,參與者均會中斷事務(wù)」
階段 3:do Commit
- 該階段進行真正的事務(wù)提交,分為以下三種情況
- 「情況 1」:階段 2 所有參與者均反饋 ack 響應(yīng),執(zhí)行真正的事務(wù)提交
- 如果協(xié)調(diào)者處于工作狀態(tài),則向所有參與者發(fā)出 do Commit 請求,參與者收到 do Commit 請求后,會正式執(zhí)行事務(wù)提交,并釋放整個事務(wù)期間占用的資源
- 各參與者向協(xié)調(diào)者反饋 ack 完成的消息,協(xié)調(diào)者收到所有參與者反饋的 ack 消息后,即完成事務(wù)提交
- 「情況 2」:階段 2 任何一個參與者反饋 no,或者等待超時后協(xié)調(diào)者尚無法收到所有參與者的反饋,即中斷事務(wù)
- 如果協(xié)調(diào)者處于工作狀態(tài),向所有參與者發(fā)出 abort 請求,參與者使用階段 1 中的 undo 信息執(zhí)行回滾操作,并釋放整個事務(wù)期間占用的資源
- 各參與者向協(xié)調(diào)者反饋 ack 完成的消息,協(xié)調(diào)者收到所有參與者反饋的 ack 消息后,即完成事務(wù)中斷
- 「情況 3」:協(xié)調(diào)者與參與者網(wǎng)絡(luò)出現(xiàn)問題
- 「參與者在協(xié)調(diào)者發(fā)出 do Commit 或 abort 請求等待超時,仍會繼續(xù)執(zhí)行事務(wù)提交」
優(yōu)缺點
優(yōu)點:在第二階段,在等待超時后協(xié)調(diào)者或參與者會中斷事務(wù)
優(yōu)點:在第三階段,避免了協(xié)調(diào)者單點問題,在協(xié)調(diào)者出現(xiàn)問題時,參與者會繼續(xù)提交事務(wù)(同時也是個缺點)
缺點:數(shù)據(jù)不一致問題依然存在,在第三階段,如果協(xié)調(diào)者請求中斷事務(wù),而協(xié)調(diào)者無法與參與者正常通信,會導(dǎo)致參與者繼續(xù)提交事務(wù),造成數(shù)據(jù)不一致
柔性事務(wù)TCC (XA模式在服務(wù)級別的實現(xiàn))
Try階段:需要做資源的檢查和預(yù)留。在扣錢場景下,Try 要做的事情是就是檢查賬戶可用余額是否充足,再凍結(jié)賬戶的資金。Try 方法執(zhí)行之后,賬號余額雖然還是100,但是其中 30 元已經(jīng)被凍結(jié)了,不能被其他事務(wù)使用
Confirm階段:扣減 Try 階段凍結(jié)的資金,Confirm 方法執(zhí)行之后,賬號在一階段中凍結(jié)的 30 元已經(jīng)被扣除,賬號 A 余額變成 70 元
Cancel階段:回滾的話,就需要在 Cancel 方法內(nèi)釋放一階段 Try 凍結(jié)的 30 元,使賬號的余額回到初始狀態(tài),100 元全部可用
AT模式(阿里分布式框架seata)
一階段:提交
- 在一階段,Seata 會攔截“業(yè)務(wù) SQL”,首先解析SQL語義,找到“業(yè)務(wù) SQL”要更新的業(yè)務(wù)數(shù)據(jù),在業(yè)務(wù)數(shù)據(jù)被更新前,將其保存成“before image”,然后執(zhí)行“業(yè)務(wù) SQL”更新業(yè)務(wù)數(shù)據(jù),在業(yè)務(wù)數(shù)據(jù)更新之后,再將其保存成“after image”,最后生成行鎖。以上操作全部在一個數(shù)據(jù)庫事務(wù)內(nèi)完成,這樣保證了一階段操作的原子性
二階段提交或回滾
- 二階段如果是提交的話,因為“業(yè)務(wù) SQL”在一階段已經(jīng)提交至數(shù)據(jù)庫, 所以 Seata 框架只需將一階段保存的快照數(shù)據(jù)和行鎖刪掉,完成數(shù)據(jù)清理即可
- 二階段如果是回滾的話,Seata 就需要回滾一階段已經(jīng)執(zhí)行的“業(yè)務(wù) SQL”,還原業(yè)務(wù)數(shù)據(jù)
- 回滾方式便是用“before image”還原業(yè)務(wù)數(shù)據(jù);但在還原前要首先要校驗臟寫,對比“數(shù)據(jù)庫當(dāng)前業(yè)務(wù)數(shù)據(jù)”和 “after image”,如果兩份數(shù)據(jù)完全一致就說明沒有臟寫,可以還原業(yè)務(wù)數(shù)據(jù),如果不一致就說明有臟寫,出現(xiàn)臟寫就需要轉(zhuǎn)人工處理
事件通知(事務(wù)消息)
同步通知
- 人的慣性思維都會考慮到同步調(diào)用,這是簡單易實現(xiàn)的方案。但是相對第三方系統(tǒng),其是不可靠的,內(nèi)部處理超時,網(wǎng)絡(luò)斷開,很容易出事故。而且等待接口返回,是個阻塞過程,影響系統(tǒng)性能
異步回調(diào)通知
相對同步通知,它的處理接口是異步回調(diào)的。因此可以避免超時處理,超時返回的問題
考慮到回調(diào)時接口報錯則需要發(fā)起重試回調(diào),因此需要加入重試機制
消息隊列
- 消息隊列可以解耦服務(wù),并且解決了錯誤重試的問題
- 因為調(diào)接口會出錯或者重復(fù)調(diào)用,需要保證接口冪等性
- 普通消息處理存在的一致性問題:發(fā)送者業(yè)務(wù)邏輯處理成功 -> MQ存儲消息成功 -> 但是MQ處理超時 -> 從而ACK確認失敗 -> 導(dǎo)致發(fā)送者本地事務(wù)回滾,但實際MQ是處理成功
- 如果存在處理返回結(jié)果也可以通過消息隊列回傳
事務(wù)狀態(tài)表+消息隊列方案
- 基于本地消息的最終一致性方案的最核心做法就是在執(zhí)行業(yè)務(wù)操作的時候,記錄一條消息數(shù)據(jù)到DB,并且消息數(shù)據(jù)的記錄與業(yè)務(wù)數(shù)據(jù)的記錄必須在同一個事務(wù)內(nèi)完成
- 在記錄完成后消息數(shù)據(jù)后,可以通過一個定時任務(wù)到DB中去輪訓(xùn)狀態(tài)為待發(fā)送的消息,然后將消息投遞給MQ。這個過程中可能存在消息投遞失敗的可能,此時就依靠重試機制來保證,直到成功收到MQ的ACK確認之后,再將消息狀態(tài)更新或者消息清除
- 同樣也需要保障接口的冪等性
歡迎指正文中錯誤
參考文章
- 分布式理論之一:Paxos算法的通俗理解[2]
- 分布式事務(wù)一致性解決方案[3]
- 2PC和3PC[4]
- 還不理解“分布式事務(wù)”?這篇給你講清楚![5]
- 分布式事務(wù)——消息最終一致性方案[6]
- 分布式事務(wù)?No, 最終一致性[7]
- 分布式事務(wù)的4種模式[8]
- 分布式事務(wù) Seata(二) 理解什么是AT、TCC、Saga[9]
Reference
[1]架構(gòu)篇:分布式理論CAP、BASE:
https://juejin.cn/post/6948809101392478245
[2]分布式理論之一:Paxos算法的通俗理解:
https://www.cnblogs.com/esingchan/p/3917718.html
[3]分布式事務(wù)一致性解決方案:
https://www.cnblogs.com/williamjie/p/11200885.html
[4]2PC和3PC:
https://blog.csdn.net/skyie53101517/article/details/80741868
[5]還不理解“分布式事務(wù)”?這篇給你講清楚!:
https://www.cnblogs.com/zjfjava/p/10425335.html
[6]分布式事務(wù)——消息最終一致性方案:
https://www.jianshu.com/p/04bad986a4a2
[7]分布式事務(wù)?No, 最終一致性:
https://zhuanlan.zhihu.com/p/25933039
[8]分布式事務(wù)的4種模式:
https://zhuanlan.zhihu.com/p/141645172
[9]分布式事務(wù) Seata(二) 理解什么是AT、TCC、Saga:
https://www.jianshu.com/p/f2caa8737b7b






































