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