分佈式理論系列html
從ACID到CAP到BASEnode
複製、分片和路由數據庫
副本更新策略segmentfault
負載均衡算法及手段服務器
本文主要講述2PC及3PC,以及Paxos以及Raft協議。併發
操做原子性與副本一致性
)2PC協議用於保證屬於多個數據分片上的操做的原子性。這些數據分片可能分佈在不一樣的服務器上,2PC協議保證多臺服務器上的操做要麼所有成功,要麼所有失敗。負載均衡
Paxos協議用於保證同一個數據分片的多個副本之間的數據一致性。當這些副本分佈到不一樣的數據中心時,這個需求尤爲強烈。分佈式
Two-Phase Commit,兩階段提交
(1)事務詢問
協調者向全部的參與者發送事務內容,詢問是否能夠執行事務提交操做,並開始等待各參與者的響應
(2)執行事務
各參與者節點執行事務操做,並將Undo和Redo信息計入事務日誌中
(3)各參與者向協調者反饋事務詢問的響應
若是參與者成功執行了事務操做,那麼就反饋給協調者Yes響應,表示事務能夠執行;若是參與者沒有成功執行事務,那麼就反饋給協調者No響應,表示事務不能夠執行。
若是全部參與者的反饋都是Yes響應,那麼
A、發送提交請求
協調者向全部參與者節點發出Commit請求
B、事務提交
參與者接收到Commit請求後,會正式執行事務提交操做,並在完成提交以後釋放在整個事務執行期間佔用的事務資源
C、反饋事務提交結果
參與者在完成事務提交以後,向協調者發送ACK信息
D、完成事務
協調者接收到全部參與者反饋的ACK消息後,完成事務
任何一個參與者反饋了No響應,或者在等待超時以後,協調者尚沒法接收到全部參與者的反饋響應,那麼就會中斷事務。
A、發送回滾請求
協調者向全部參與者節點發出Rollback請求
B、事務回滾
參與者接收到rollback請求後,會利用其在階段一中記錄的Undo信息來執行事務回滾操做,並在完成回滾以後釋放整個事務執行期間佔用的資源
C、反饋事務回滾結果
參與者在完成事務回滾以後,向協調者發送ACK信息
D、中斷事務
協調者接收到全部參與者反饋的ACK信息後,完成事務中斷
優勢:原理簡單、實現方便
缺點:同步阻塞、單點問題、數據不一致、太過保守
(1)同步阻塞
同步阻塞會極大地限制分佈式系統的性能。在二階段提交的執行過程當中,全部參與該事務操做的邏輯都處於阻塞狀態,各個參與者在等待其餘參與者響應的過程當中,將沒法進行其餘任何操做。
(2)單點問題
一旦協調者出現問題,那麼整個二階段提交流程將沒法運轉,更爲嚴重的是,若是是在階段二中出現問題,那麼其餘參與者將會一直處於鎖定事務資源的狀態中,沒法繼續完成事務操做。
(3)數據不一致
在階段二,當協調者向全部參與者發送commit請求以後,發生了局部網絡異常或協調者在還沒有發完commit請求以前自身發生了崩潰,致使最終只有部分參與者接收到了commit請求,因而這部分參與者執行事務提交,而沒收到commit請求的參與者則沒法進行事務提交,因而整個分佈式系統出現了數據不一致性現象。
(4)太過保守
若是參與者在與協調者通訊期間出現故障,協調者只能靠超時機制來判斷是否須要中斷事務,這個策略比較保守,須要更爲完善的容錯機制,任意一個節點的失敗都會致使整個事務的失敗。
Three-Phase Commit,三階段提交,分爲CanCommit、PreCommit、do Commit三個階段。
爲了不在通知全部參與者提交事務時,其中一個參與者crash不一致時,就出現了三階段提交的方式。三階段提交在兩階段提交的基礎上增長了一個preCommit的過程,當全部參與者收到preCommit後,並不執行動做,直到收到commit或超過必定時間後才完成操做。
(1)事務詢問
協調者向各參與者發送CanCommit的請求,詢問是否能夠執行事務提交操做,並開始等待各參與者的響應
(2)參與者向協調者反饋詢問的響應
參與者收到CanCommit請求後,正常狀況下,若是自身認爲能夠順利執行事務,那麼會反饋Yes響應,並進入預備狀態,不然反饋No。
若是協調者接收到各參與者反饋都是Yes,那麼執行事務預提交
A、發送預提交請求
協調者向各參與者發送preCommit請求,並進入prepared階段
B、事務預提交
參與者接收到preCommit請求後,會執行事務操做,並將Undo和Redo信息記錄到事務日記中
C、各參與者向協調者反饋事務執行的響應
若是各參與者都成功執行了事務操做,那麼反饋給協調者Ack響應,同時等待最終指令,提交commit或者終止abort
若是任何一個參與者向協調者反饋了No響應,或者在等待超時後,協調者沒法接收到全部參與者的反饋,那麼就會中斷事務。
A、發送中斷請求
協調者向全部參與者發送abort請求
B、中斷事務
不管是收到來自協調者的abort請求,仍是等待超時,參與者都中斷事務
A、發送提交請求
假設協調者正常工做,接收到了全部參與者的ack響應,那麼它將從預提交階段進入提交狀態,並向全部參與者發送doCommit請求
B、事務提交
參與者收到doCommit請求後,正式提交事務,並在完成事務提交後釋放佔用的資源
C、反饋事務提交結果
參與者完成事務提交後,向協調者發送ACK信息
D、完成事務
協調者接收到全部參與者ack信息,完成事務
假設協調者正常工做,而且有任一參與者反饋No,或者在等待超時後沒法接收全部參與者的反饋,都會中斷事務
A、發送中斷請求
協調者向全部參與者節點發送abort請求
B、事務回滾
參與者接收到abort請求後,利用undo日誌執行事務回滾,並在完成事務回滾後釋放佔用的資源
C、反饋事務回滾結果
參與者在完成事務回滾以後,向協調者發送ack信息
D、中斷事務
協調者接收到全部參與者反饋的ack信息後,中斷事務。
階段三可能出現的問題:
協調者出現問題、協調者與參與者之間網絡出現故障。不論出現哪一種狀況,最終都會致使參與者沒法及時接收到來自協調者的doCommit或是abort請求,針對這種狀況,參與者都會在等待超時後,繼續進行事務提交(timeout後中斷事務)。
優勢:下降參與者阻塞範圍,並可以在出現單點故障後繼續達成一致
缺點:引入preCommit階段,在這個階段若是出現網絡分區,協調者沒法與參與者正常通訊,參與者依然會進行事務提交,形成數據不一致。
基於消息傳遞且具備高度容錯性的一致性算法。Paxos算法要解決的問題就是如何在可能發生幾起宕機或網絡異常的分佈式系統中,快速且正確地在集羣內部對某個數據的值達成一致,而且保證不論發生以上任何異常,都不會破壞整個系統的一致性。
拜占庭問題:消息不完整或者被篡改。Paxos在維持領導者選舉或者變量修改一致性上,採起一種相似議會投票的過半贊成機制,好比設定一個領導者,須要將此看作一個議案,徵求過半贊成,每一個節點經過一個議案會有編號記錄,再次收到此領導者的不一樣人選,發現已經有編號記錄便駁回,最後以多數經過的結果爲準。
咱們舉個簡單的例子,來闡述一下Paxos的基本思想:假設咱們有5臺計算機A、B、C、D、E,每臺計算機保存着公司CEO的信息,如今CEO任期到了,須要進行新一界選舉了。
A計算機發起一個選舉議案,提議CEO爲「張三」,若是沒有其餘候選人議案,也沒有網絡問題,只要其中半數以上計算機收到並經過議案,那麼最終「張三」當選CEO。因爲是分佈式環境,併發請求、機器故障、網絡故障等問題是常態,若是A和E同時提交選舉議案,A提名「張三」,E提名「李四」,那麼確定會涉及多計算機的一致性問題了:假設A、B、C先收到A的議案,D、E先收到E的議案,那麼A繼續提交給D時,D告訴它已經先收到E的議案了,所以駁回了A的請求。一樣E繼續提交給A、B、C時也碰到相同的問題。
咱們能夠經過「在每臺計算機同時接受議案提交時設置一個編號,編號先的經過,編號後的駁回」的方式來實現。議案提交上去後,發現A、B、C投票「張三」爲CEO,D、E投票「李四」爲CEO,少數服從多數,所以最後結果爲「張三」當選CEO。
若是是C計算機發生了網絡問題或者故障,雙方投票相同,那麼選舉沒法完成。
若是C計算機發生了網絡問題或者故障,A、B、D投票「張三」,E投票「李四」,那麼結果爲「張三」當選,而C對於這些狀況一無所知,可是當C計算機恢復正常時,他會發起一個「詢問誰是CEO」的議案獲取最新信息。簡言之,Paxos對每一個節點的併發修改採起編號記錄的方式保持一致性,對多個節點的併發修改採起少數服從多數的方式保持一致性。Paxos有點相似分佈式二階段提交方式,可是又不一樣,二階段提交不能是多數節點贊成,必須是所有贊成。爲了遵照過半節點贊成的約束,Paxos算法每每要求節點總數爲奇數。
Paxos 算法解決的問題是在一個可能發生上述異常的分佈式系統中如何就某個值達成一致,保證不論發生以上任何異常,都不會破壞決議的一致性。一個典型的場景是,在一個分佈式數據庫系統中,若是各節點的初始狀態一致,每一個節點都執行相同的操做序列,那麼他們最後能獲得一個一致的狀態。爲保證每一個節點執行相同的命令序列,須要在每一條指令上執行一個「一致性算法」以保證每一個節點看到的指令一致。一個通用的一致性算法能夠應用在許多場景中,是分佈式計算中的重要問題。從20世紀80年代起對於一致性算法的研究就沒有中止過。
簡單說來,Paxos的目的是讓整個集羣的結點對某個值的變動達成一致。Paxos算法基本上來講是個民主選舉的算法——大多數的決定會成個整個集羣的統一決定。任何一個點均可以提出要修改某個數據的提案,是否經過這個提案取決於這個集羣中是否有超過半數的結點贊成(因此Paxos算法須要集羣中的結點是單數)。
這個算法有兩個階段(假設這個有三個結點:A,B,C):
A把申請修改的請求Prepare Request發給全部的結點A,B,C。注意,Paxos算法會有一個Sequence Number(你能夠認爲是一個提案號,這個數不斷遞增,並且是惟一的,也就是說A和B不可能有相同的提案號),這個提案號會和修改請求一同發出,任何結點在「Prepare階段」時都會拒絕其值小於當前提案號的請求。因此,結點A在向全部結點申請修改請求的時候,須要帶一個提案號,越新的提案,這個提案號就越是是最大的。
若是接收結點收到的提案號n大於其它結點發過來的提案號,這個結點會迴應Yes(本結點上最新的被批准提案號),並保證不接收其它<n的提案。這樣一來,結點上在Prepare階段里老是會對最新的提案作承諾。
優化:在上述 prepare 過程當中,若是任何一個結點發現存在一個更高編號的提案,則須要通知 提案人,提醒其中斷此次提案。
若是提案者A收到了超過半數的結點返回的Yes,而後他就會向全部的結點發布Accept Request(一樣,須要帶上提案號n),若是沒有超過半數的話,那就返回失敗。
當結點們收到了Accept Request後,若是對於接收的結點來講,n是最大的了,那麼,它就會經過request(修改這個值),若是發現本身有一個更大的提案號,那麼,結點就會拒絕request(拒絕修改)。
咱們能夠看以,這彷佛就是一個「兩段提交」的優化。其實,2PC/3PC都是分佈式一致性算法的殘次版本,Google Chubby的做者Mike Burrows說過這個世界上只有一種一致性算法,那就是Paxos,其它的算法都是殘次品。
咱們還能夠看到:對於同一個值的在不一樣結點的修改提案就算是在接收方被亂序收到也是沒有問題的。
解決paxos的實現難度
)Paxos 相比 Raft 比較複雜和難以理解。角色扮演和流程比 Raft 都要囉嗦。好比 Agreement 這個流程,在 Paxos 裏邊:Client 發起請求舉薦 Proposer 成爲 Leader,Proposer 而後向全局 Acceptors 尋求確認,Acceptors 所有贊成 Proposer 後,Proposer 的 Leader 地位得已認可,Acceptors 還得再向Learners 進行全局廣播來同步。而在 Raft 裏邊,只有 Follower/Candidate/Leader 三種角色,角色自己表明狀態,角色之間進行狀態轉移是一件很是自由民主的事情。Raft雖然有角色之分可是是全民參與進行選舉的模式;可是在Paxos裏邊,感受更像議員參政模式。
follower、candidate、leader。
最開始你們都是follower,當follower監聽不到leader,就能夠本身成爲candidate,發起投票
follower成爲candidate的超時時間,每一個follower都在150ms and 300ms之間隨機,以後看誰先timeout,誰就先成爲candidate,而後它會先投本身一票,再向其餘節點發起投票邀請。
若是其餘節點在這輪選舉尚未投過票,那麼就給candidate投票,而後重置本身的選舉timeout。
若是獲得大多數的投票就成爲leader,以後按期開始向follower發送心跳。
若是兩個follower同時成爲candidate的話,若是最後獲得的票數相同,則等待其餘follower的選擇timeout以後成爲candidate,繼續開始新一輪的選舉。
leader把變更的log藉助心跳同步給follower,過半回覆以後才成功提交,以後再下一次心跳以後,follower也commit變更,在本身的node上生效。
分裂以後,另外一個分區的follower接受不到leader的timeout,而後會有一個先timeout,成爲candidate,最後成爲leader。
因而兩個分區就有了兩個leader。
當客戶端有變更時,其中的leader因爲沒法收到過半的提交,則保持未提交狀態。有的leader的修改,能夠獲得過半的提交,則能夠修改生效。
當分裂恢復以後,leader開始對比選舉的term,發現有更高的term存在時,他們會撤銷未提交的修改,而後以最新的爲準。
解決f容錯的2f+1成本問題
)Kafka並無使用Zab或Paxos協議的多數投票機制來保證主備數據的一致性,而是提出了ISR的機制(In-Sync Replicas)的機制來保證數據一致性。
ISR認爲對於2f+1個副原本說,多數投票機制要求最多隻能容許f個副本發生故障,若是要支持2個副本的容錯,則須要至少維持5個副本,對於消息系統的場景來講,效率過低。
ISR的運行機制以下:將全部次級副本數據分到兩個集合,其中一個被稱爲ISR集合,這個集合備份數據的特色是即時和主副本數據保持一致,而另一個集合的備份數據容許其消息隊列落後於主副本的數據。在作主備切換時,只容許從ISR集合中選擇主副本,只有ISR集合內全部備份都寫成功才能認爲此次寫入操做成功。在具體實現時,kafka利用zookeeper來保持每一個ISR集合的信息,當ISR集合內成員變化時,相關構件也便於通知。經過這種方式,若是設定ISR集合大小爲f+1,那麼能夠最多容許f個副本故障,而對於多數投票機制來講,則須要2f+1個副本才能達到相同的容錯性。