分佈式一致性問題
分佈式一致性問題大體可以分爲數據一致性和事務一致性,說白了,數據一致性就是多個進程如何對數據的修改達成一致,事務的一致性就是如何將對多個進程的執行處理視爲原子性的操作,統一提交或回滾,爲了解決以上幾個問題,業界提出了很多經典的算法,如二階段提交,三階段提交,Paxos等,本篇wiki將對比這三種算法並着重講解paxos,引出zookeeper的相關算法基礎。
二階段提交
目前的大部分關係型數據庫採用此協議實現分佈式事務,在此協議中,角色包括協調者和參與者,協議主要包括二個階段,事務請求階段,事務提交階段
事務請求階段
1)協調者向所有參與者發起事務請求並等待響應
2)所有參與者執行事務操作並響應執行結果,成功執行事務,則響應成功,反之響應失敗
事務提交階段
1)如果所有參與者都響應成功,則協調者向所有參與者發起事務提交
2)參與者執行事務提交併響應提交結果
異常回滾
由協調者向所有參與者發起事務回滾,主要有以下場景:
1)事務請求階段等待響應超時或有參與者響應失敗
優缺點
優點:簡單
缺點:單點問題,存在數據不一致,同步阻塞等,歸結起來就是沒有有效的容錯機制,比如協調者掛了就不可用了,或者事務提交階段,網絡異常,有的參與者沒收到事務提交請求有的收到了,便出現了數據不一致等。
三階段提交
三階段提交其實是對二階段的改良,主要是把二階段中的事務請求階段拆分成了事務詢問與事務執行階段
事務詢問階段
1)協調者向所有參與者詢問事務能否執行
2)參與者響應事務可否執行結果,可以返回yes,反之返回no
事務執行階段
1)如果參與者返回的全是yes,則向所有參與者發起事務執行請求
2)參與者執行事務並返回執行結果
3)等待協調者發送事務提交或異常中斷請求,如果沒有收到協調者指令,則執行事務提交
事務提交階段
1)如果所有參與者返回的事務執行結果全是yes,則發起事務提交
2)參與者執行事務提交併返回結果
異常回滾
1)事務執行階段參與者返回No或超時
2)事務提交階段參與者返回no或超時
優缺點
優點:部分解決了二階段的單點問題,如協調者故障後依然可以提交事務
缺點:引用新的問題,有可能在事務應該回滾的情況下,調協者故障,這時卻依然提交了事務
Paxos算法
此算法是基於消息傳遞且有高度容錯機制的一致性算法,既然是基於消息傳遞,那麼就會想到拜占庭將軍問題(即消息丟失或被篡改),因爲現實中系統大多都會部署在一個局域網內,所以假設不存在拜占庭將軍問題。算法本身是比較簡單的,但算法的描述和推理比較複雜,所以這裏分開來講解。
算法內容
paxos中的角色包括proposer(提案的提出者),acceptor(提案是否通過的表決者),learner(提案獲取者),不同的角色不一定是不同的進程,也可能一個進程擁有多個角色
消息以提案表示:<編號,value>,編號爲全局遞增唯一
算法也包括三個階段,詢問階段,表決階段,學習階段:
詢問階段
1)proposer獲取一個提案編號M,根據M向一個acceptor集合(超過半數)的所有acceptor詢問當前是否有已經被批准的提案
2)acceptor判斷M是否大於自己已經批准過的所有的提案編號,如果是,則返回已批准的編號<M且最大的提案,如果否,可以不響應,如果acceptor之前沒有批准過任何提案,則返回空
3)acceptor不再批准編號小於M的提案
4)proposer收到acceptor的響應(acceptor超過半數),如果爲空,則自己可以任意設置value的值,如果返回了已批准的提案,則必須以此編號最大的提案的value作爲自己的value,生成<M,value>作爲自己要發起的提案,如果未超過半數,則重複此階段
表決階段
1)proposer確定了自己的提案<M,value>後,會向一個acceptor集合(超過半數且不一定是上一個集合)的所有acceptor發起提案
2)只要acceptor沒有批准過比M編號更小的提案,就可以批准該提案
3)超過半數的acceptor批准了提案後,就對value的值達到了一致性,沒有的話,則從詢問階段重複
整個過程中:
1)proposer可以提出多個提案
2)acceptor可以批准多個提案
只要各角色嚴格重複上面的過程,就可以達到最終一致性,但這裏在實現時有一種極端場景,如下:
比如proposer1,proposer2交替的向acceptor1,acceptor2,acceptor3發起請求,proposer1向acceptor以m0爲編號詢問,完成之後proposer2又以m1爲編號詢問,完成之後acceptor不再批准比m1編號小的提案了,這是proposer1的提案被否決了,這時proposer1又以m2以編號詢問,完成後acceptor又不再批准m1了,一直這樣下去,就進入死循環了,所以在實現過程中,會選擇主proposer,只有主proposer纔可以發生提案。
學習階段
提案被確定後就可以通過learner獲取了,獲取的方式有很多種,可以根據具體情況選擇,如:
1)acceptor批准提案後,立即發送給所有learner
2)acceptor批准後都把提案發給特定的learner,然後由此learner通知其它learner
3)acceptor批准後都把提案發給特定的learner集合,由此集合通知其它learner
算法推理
下面通過數學模型與條件的加強來推導上面算法的內容結論
1)不考慮拜占庭將軍問題,如果情況極端一點,只有一個提案被提出,毫無疑問,應該被批准通過,由此得到以下約束:
約束1:acceptor必須批准它收到的第一個提案
這裏考慮以下問題:
這種情況下,滿足了約束1,但最終無法選定到底是哪個提案,所以這時需要對約束1做一些加強:
約束2:一個提案的選定,需要半數以上的acceptor批准
約束2隱含了一個內容,既然可以有多個proposer提出提案,選定必須是超過半數的acceptor批准纔可以,就表示一個acceptor可以批准多了提案,在paxos中,提案由全局唯一編號m和value表示<m,value>,paxos算法其實就是選value的過程,對value的值達成最終一致性
所以,只有當value確定了,整個過程纔算結果,那麼acceptor可以批准多個提案,在批准了多個不同的提案後,還能最終確定出value,這說明,被批准的多個提案,value是相同的,所以這裏得到了約束3:
約束3:如果<m0,value0>提案被選定,那麼所有比m0編號高的且被批准的提案,value必須是value0
現在假設這樣一種場景:
1)proposer1向acceptor1,2,3提出了一個提案<m0,value0>且被批准了,這時其實就已經被選定了
2)proposer2向acceptor4提出了一個提案<m1,value1>,m1>m0,根據約束1,acceptor4就得批准,這樣就與約束3矛盾了,所以這裏需要再強化條件約束:
約束4:如果一個提案<m0,value0>被選定後,任何proposer提出的編號更高的提案,value必須是value0
現在,需要證明約束4:
1)假設m0到mn-1的提案value都是value0,證明mn的提案<mn,valuen>value也是value0,即valuen=value0
2)因爲m0被選定了,所以表示超過半數的acceptor都批准了m0,那麼根據約束3,m0到mn-1的所有被批准的提案value也是value0
3)如果這時mn提案也可以被批准,則意味着如下2種場景:
a)超過半數的acceptor沒批准過比mn小的提案
b)超過半數的acceptor批准的比mn小的提案中,編號最大的那個提案value是valuen即valuen=value0,因爲<m0,value0>已經被選定了,所以後面被批准的提案的value肯定是value0,這時如果mn提案可以被批准,valuen=value0纔可能被批准
綜上,推導出了算法的結論