當咱們在生產線上用一臺服務器來提供數據服務的時候,我會遇到以下的兩個問題:html
1)一臺服務器的性能不足以提供足夠的能力服務於全部的網絡請求。算法
2)咱們老是懼怕咱們的這臺服務器停機,形成服務不可用或是數據丟失。數據庫
因而咱們不得不對咱們的服務器進行擴展,加入更多的機器來分擔性能上的問題,以及來解決單點故障問題。 一般,咱們會經過兩種手段來擴展咱們的數據服務:安全
1)數據分區:就是把數據分塊放在不一樣的服務器上(如:uid % 16,一致性哈希等)。服務器
2)數據鏡像:讓全部的服務器都有相同的數據,提供至關的服務。網絡
對於第一種狀況,咱們沒法解決數據丟失的問題,單臺服務器出問題時,會有部分數據丟失。因此,數據服務的高可用性只能經過第二種方法來完成——數據的冗餘存儲(通常工業界認爲比較安全的備份數應該是3份,如:Hadoop和Dynamo)。 可是,加入更多的機器,會讓咱們的數據服務變得很複雜,尤爲是跨服務器的事務處理,也就是跨服務器的數據一致性。這個是一個很難的問題。 讓咱們用最經典的Use Case:「A賬號向B賬號匯錢」來講明一下,熟悉RDBMS事務的都知道從賬號A到賬號B須要6個操做:併發
爲了數據的一致性,這6件事,要麼都成功作完,要麼都不成功,並且這個操做的過程當中,對A、B賬號的其它訪問必需鎖死,所謂鎖死就是要排除其它的讀寫操做,否則會有髒數據的問題,這就是事務。那麼,咱們在加入了更多的機器後,這個事情會變得複雜起來:app
1)在數據分區的方案中:若是A賬號和B賬號的數據不在同一臺服務器上怎麼辦?咱們須要一個跨機器的事務處理。也就是說,若是A的扣錢成功了,但B的加錢不成功,咱們還要把A的操做給回滾回去。這在跨機器的狀況下,就變得比較複雜了。框架
2)在數據鏡像的方案中:A賬號和B賬號間的匯款是能夠在一臺機器上完成的,可是別忘了咱們有多臺機器存在A賬號和B賬號的副本。若是對A賬號的匯錢有兩個併發操做(要匯給B和C),這兩個操做發生在不一樣的兩臺服務器上怎麼辦?也就是說,在數據鏡像中,在不一樣的服務器上對同一個數據的寫操做怎麼保證其一致性,保證數據不衝突?異步
同時,咱們還要考慮性能的因素,若是不考慮性能的話,事務獲得保證並不困難,系統慢一點就好了。除了考慮性能外,咱們還要考慮可用性,也就是說,一臺機器沒了,數據不丟失,服務可由別的機器繼續提供。 因而,咱們須要重點考慮下面的這麼幾個狀況:
1)容災:數據不丟、結點的Failover
2)數據的一致性:事務處理
3)性能:吞吐量 、 響應時間
前面說過,要解決數據不丟,只能經過數據冗餘的方法,就算是數據分區,每一個區也須要進行數據冗餘處理。這就是數據副本:當出現某個節點的數據丟失時能夠從副本讀到,數據副本是分佈式系統解決數據丟失異常的惟一手段。因此,在這篇文章中,簡單起見,咱們只討論在數據冗餘狀況下考慮數據的一致性和性能的問題。簡單說來:
1)要想讓數據有高可用性,就得寫多份數據。
2)寫多份的問題會致使數據一致性的問題。
3)數據一致性的問題又會引起性能問題
這就是軟件開發,按下了葫蘆起了瓢。
提及數據一致性來講,簡單說有三種類型(固然,若是細分的話,還有不少一致性模型,如:順序一致性,FIFO一致性,會話一致性,單讀一致性,單寫一致性,但爲了本文的簡單易讀,我只說下面三種):
1)Weak 弱一致性:當你寫入一個新值後,讀操做在數據副本上可能讀出來,也可能讀不出來。好比:某些cache系統,網絡遊戲其它玩家的數據和你沒什麼關係,VOIP這樣的系統,或是百度搜索引擎(呵呵)。
2)Eventually 最終一致性:當你寫入一個新值後,有可能讀不出來,但在某個時間窗口以後保證最終能讀出來。好比:DNS,電子郵件、Amazon S3,Google搜索引擎這樣的系統。
3)Strong 強一致性:新的數據一旦寫入,在任意副本任意時刻都能讀到新值。好比:文件系統,RDBMS,Azure Table都是強一致性的。
從這三種一致型的模型上來講,咱們能夠看到,Weak和Eventually通常來講是異步冗餘的,而Strong通常來講是同步冗餘的,異步的一般意味着更好的性能,但也意味着更復雜的狀態控制。同步意味着簡單,但也意味着性能降低。 好,讓咱們由淺入深,一步一步地來看有哪些技術:
首先是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,由於若是寫Master本身失敗後,還要回滾Slave,此時若是回滾Slave失敗,就得手工訂正數據了)你能夠看到,若是Master-Slave須要作成強一致性有多複雜。
Master-Master,又叫Multi-master,是指一個系統存在兩個或多個Master,每一個Master都提供read-write服務。這個模型是Master-Slave的增強版,數據間同步通常是經過Master間的異步完成,因此是最終一致性。 Master-Master的好處是,一臺Master掛了,別的Master能夠正常作讀寫服務,他和Master-Slave同樣,當數據沒有被複制到別的Master上時,數據會丟失。不少數據庫都支持Master-Master的Replication的機制。
另外,若是多個Master對同一個數據進行修改的時候,這個模型的惡夢就出現了——對數據間的衝突合併,這並非一件容易的事情。看看Dynamo的Vector Clock的設計(記錄數據的版本號和修改者)就知道這個事並不那麼簡單,並且Dynamo對數據衝突這個事是交給用戶本身搞的。就像咱們的SVN源碼衝突同樣,對於同一行代碼的衝突,只能交給開發者本身來處理。(在本文後後面會討論一下Dynamo的Vector Clock)
這個協議的縮寫又叫2PC,中文叫兩階段提交。在分佈式系統中,每一個節點雖然能夠知曉本身的操做時成功或者失敗,卻沒法知道其餘節點的操做的成功或失敗。當一個事務跨越多個節點時,爲了保持事務的ACID特性,須要引入一個做爲協調者的組件來統一掌控全部節點(稱做參與者)的操做結果並最終指示這些節點是否要把操做結果進行真正的提交(好比將更新後的數據寫入磁盤等等)。 兩階段提交的算法以下:
第一階段:
第二階段:
咱們能夠看到,2PC說白了就是第一階段作Vote,第二階段作決定的一個算法,也能夠看到2PC這個事是強一致性的算法。在前面咱們討論過Master-Slave的強一致性策略,和2PC有點類似,只不過2PC更爲保守一些——先嚐試再提交。 2PC用的是比較多的,在一些系統設計中,會串聯一系列的調用,好比:A -> B -> C -> D,每一步都會分配一些資源或改寫一些數據。好比咱們B2C網上購物的下單操做在後臺會有一系列的流程須要作。若是咱們一步一步地作,就會出現這樣的問題,若是某一步作不下去了,那麼前面每一次所分配的資源須要作反向操做把他們都回收掉,因此,操做起來比較複雜。如今不少處理流程(Workflow)都會借鑑2PC這個算法,使用 try -> confirm的流程來確保整個流程的可以成功完成。 舉個通俗的例子,西方教堂結婚的時候,都有這樣的橋段:
1)牧師分別問新郎和新娘:你是否願意……無論生老病死……(詢問階段)
2)當新郎和新娘都回答願意後(鎖定一輩子的資源),牧師就會說:我宣佈大家……(事務提交)
這是多麼經典的一個兩階段提交的事務處理。 另外,咱們也能夠看到其中的一些問題, A)其中一個是同步阻塞操做,這個事情必然會很是大地影響性能。 B)另外一個主要的問題是在TimeOut上,好比,
1)若是第一階段中,參與者沒有收到詢問請求,或是參與者的迴應沒有到達協調者。那麼,須要協調者作超時處理,一旦超時,能夠看成失敗,也能夠重試。
2)若是第二階段中,正式提交發出後,若是有的參與者沒有收到,或是參與者提交/回滾後的確認信息沒有返回,一旦參與者的迴應超時,要麼重試,要麼把那個參與者標記爲問題結點剔除整個集羣,這樣能夠保證服務結點都是數據一致性的。
3)糟糕的狀況是,第二階段中,若是參與者收不到協調者的commit/fallback指令,參與者將處於「狀態未知」階段,參與者徹底不知道要怎麼辦,好比:若是全部的參與者完成第一階段的回覆後(可能所有yes,可能所有no,可能部分yes部分no),若是協調者在這個時候掛掉了。那麼全部的結點徹底不知道怎麼辦(問別的參與者都不行)。爲了一致性,要麼死等協調者,要麼重發第一階段的yes/no命令。
兩段提交最大的問題就是第3)項,若是第一階段完成後,參與者在第二階沒有收到決策,那麼數據結點會進入「不知所措」的狀態,這個狀態會block住整個事務。也就是說,協調者Coordinator對於事務的完成很是重要,Coordinator的可用性是個關鍵。 因些,咱們引入三段提交,三段提交在Wikipedia上的描述以下,他把二段提交的第一個段break成了兩段:詢問,而後再鎖資源。最後真正提交。三段提交的示意圖以下:
三段提交的核心理念是:在詢問的時候並不鎖定資源,除非全部人都贊成了,纔開始鎖資源。
理論上來講,若是第一階段全部的結點返回成功,那麼有理由相信成功提交的機率很大。這樣一來,能夠下降參與者Cohorts的狀態未知的機率。也就是說,一旦參與者收到了PreCommit,意味他知道你們其實都贊成修改了。這一點很重要。下面咱們來看一下3PC的狀態遷移圖:(注意圖中的虛線,那些F,T是Failuer或Timeout,其中的:狀態含義是 q – Query,a – Abort,w – Wait,p – PreCommit,c – Commit)
從上圖的狀態變化圖咱們能夠從虛線(那些F,T是Failuer或Timeout)看到——若是結點處在P狀態(PreCommit)的時候發生了F/T的問題,三段提交比兩段提交的好處是,三段提交能夠繼續直接把狀態變成C狀態(Commit),而兩段提交則不知所措。
其實,三段提交是一個很複雜的事情,實現起來至關難,並且也有一些問題。
看到這裏,我相信你有不少不少的問題,你必定在思考2PC/3PC中各類各樣的失敗場景,你會發現Timeout是個很是難處理的事情,由於網絡上的Timeout在不少時候讓你無所事從,你也不知道對方是作了仍是沒有作。因而你好好的一個狀態機就由於Timeout成了個擺設。
一個網絡服務會有三種狀態:1)Success,2)Failure,3)Timeout,第三個絕對是惡夢,尤爲在你須要維護狀態的時候。
Two Generals Problem 兩將軍問題是這麼一個思惟性實驗問題: 有兩支軍隊,它們分別有一位將軍領導,如今準備攻擊一座修築了防護工事的城市。這兩支軍隊都駐紮在那座城市的附近,分佔一座山頭。一道山谷把兩座山分隔開來,而且兩位將軍惟一的通訊方式就是派各自的信使來往于山谷兩邊。不幸的是,這個山谷已經被那座城市的保衛者佔領,而且存在一種可能,那就是任何被派出的信使經過山谷是會被捕。 請注意,雖然兩位將軍已經就攻擊那座城市達成共識,但在他們各自佔領山頭陣地以前,並無就進攻時間達成共識。兩位將軍必須讓本身的軍隊同時進攻城市才能取得成功。所以,他們必須互相溝通,以肯定一個時間來攻擊,並贊成就在那時攻擊。若是隻有一個將軍進行攻擊,那麼這將是一個災難性的失敗。 這個思惟實驗就包括考慮他們如何去作這件事情。下面是咱們的思考:
1)第一位將軍先發送一段消息「讓咱們在上午9點開始進攻」。然而,一旦信使被派遣,他是否經過了山谷,第一位將軍就不得而知了。任何一點的不肯定性都會使得第一位將軍攻擊猶豫,由於若是第二位將軍不能在同一時刻發動攻擊,那座城市的駐軍就會擊退他的軍隊的進攻,致使他的軍對被摧毀。
2)知道了這一點,第二位將軍就須要發送一個確認回條:「我收到您的郵件,並會在9點的攻擊。」可是,若是帶着確認消息的信使被抓怎麼辦?因此第二位將軍會猶豫本身的確認消息是否能到達。
3)因而,彷佛咱們還要讓第一位將軍再發送一條確認消息——「我收到了你的確認」。然而,若是這位信使被抓怎麼辦呢?
4)這樣一來,是否是咱們還要第二位將軍發送一個「確認收到你的確認」的信息。
靠,因而你會發現,這事情很快就發展成爲無論發送多少個確認消息,都沒有辦法來保證兩位將軍有足夠的自信本身的信使沒有被敵軍捕獲。
這個問題是無解的。兩個將軍問題和它的無解證實首先由E.A.Akkoyunlu,K.Ekanadham和R.V.Huber於1975年在《一些限制與折衷的網絡通訊設計》一文中發表,就在這篇文章的第73頁中一段描述兩個黑幫之間的通訊中被闡明。 1978年,在Jim Gray的《數據庫操做系統注意事項》一書中(從第465頁開始)被命名爲兩個將軍悖論。做爲兩個將軍問題的定義和無解性的證實的來源,這一參考被普遍說起。
這個實驗意在闡明:試圖經過創建在一個不可靠的鏈接上的交流來協調一項行動的隱患和設計上的巨大挑戰。
從工程上來講,一個解決兩個將軍問題的實際方法是使用一個可以承受通訊信道不可靠性的方案,並不試圖去消除這個不可靠性,但要將不可靠性削減到一個能夠接受的程度。好比,第一位將軍排出了100位信使並預計他們都被捕的可能性很小。在這種狀況下,無論第二位將軍是否會攻擊或者受到任何消息,第一位將軍都會進行攻擊。另外,第一位將軍能夠發送一個消息流,而第二位將軍能夠對其中的每一條消息發送一個確認消息,這樣若是每條消息都被接收到,兩位將軍會感受更好。然而咱們能夠從證實中看出,他們倆都不能確定這個攻擊是能夠協調的。他們沒有算法可用(好比,收到4條以上的消息就攻擊)可以確保防止僅有一方攻擊。再者,第一位將軍還能夠爲每條消息編號,說這是1號,2號……直到n號。這種方法能讓第二位將軍知道通訊信道到底有多可靠,而且返回合適的數量的消息來確保最後一條消息被接收到。若是信道是可靠的話,只要一條消息就好了,其他的就幫不上什麼忙了。最後一條和第一條消息丟失的機率是相等的。
兩將軍問題能夠擴展成更變態的拜占庭將軍問題 (Byzantine Generals Problem),其故事背景是這樣的:拜占庭位於如今土耳其的伊斯坦布爾,是東羅馬帝國的首都。因爲當時拜占庭羅馬帝國國土遼闊,爲了防護目的,所以每一個軍隊都分隔很遠,將軍與將軍之間只能靠信差傳消息。 在戰爭的時候,拜占庭軍隊內全部將軍必需達成一致的共識,決定是否有贏的機會纔去攻打敵人的陣營。可是,軍隊可能有叛徒和敵軍間諜,這些叛徒將軍們會擾亂或左右決策的過程。這時候,在已知有成員謀反的狀況下,其他忠誠的將軍在不受叛徒的影響下如何達成一致的協議,這就是拜占庭將軍問題。
Wikipedia上的各類Paxos算法的描述很是詳細,你們能夠去圍觀一下。
Paxos 算法解決的問題是在一個可能發生上述異常的分佈式系統中如何就某個值達成一致,保證不論發生以上任何異常,都不會破壞決議的一致性。一個典型的場景是,在一個分佈式數據庫系統中,若是各節點的初始狀態一致,每一個節點都執行相同的操做序列,那麼他們最後能獲得一個一致的狀態。爲保證每一個節點執行相同的命令序列,須要在每一條指令上執行一個「一致性算法」以保證每一個節點看到的指令一致。一個通用的一致性算法能夠應用在許多場景中,是分佈式計算中的重要問題。從20世紀80年代起對於一致性算法的研究就沒有中止過。
Notes:Paxos算法是萊斯利·蘭伯特(Leslie Lamport,就是 LaTeX 中的」La」,此人如今在微軟研究院)於1990年提出的一種基於消息傳遞的一致性算法。因爲算法難以理解起初並無引發人們的重視,使Lamport在八年後1998年從新發表到ACM Transactions on Computer Systems上(The Part-Time Parliament)。即使如此paxos算法仍是沒有獲得重視,2001年Lamport 以爲同行沒法接受他的幽默感,因而用容易接受的方法從新表述了一遍(Paxos Made Simple)。可見Lamport對Paxos算法情有獨鍾。近幾年Paxos算法的廣泛使用也證實它在分佈式一致性算法中的重要地位。2006年Google的三篇論文初現「雲」的端倪,其中的Chubby Lock服務使用Paxos做爲Chubby Cell中的一致性算法,Paxos的人氣今後一路狂飆。(Lamport 本人在 他的blog 中描寫了他用9年時間發表這個算法的前先後後)
注:Amazon的AWS中,全部的雲服務都基於一個ALF(Async Lock Framework)的框架實現的,這個ALF用的就是Paxos算法。我在Amazon的時候,看內部的分享視頻時,設計者在內部的Principle Talk裏說他參考了ZooKeeper的方法,但他用了另外一種比ZooKeeper更易讀的方式實現了這個算法。
簡單說來,Paxos的目的是讓整個集羣的結點對某個值的變動達成一致。Paxos算法基本上來講是個民主選舉的算法——大多數的決定會成個整個集羣的統一決定。任何一個點均可以提出要修改某個數據的提案,是否經過這個提案取決於這個集羣中是否有超過半數的結點贊成(因此Paxos算法須要集羣中的結點是單數)。
這個算法有兩個階段(假設這個有三個結點:A,B,C):
第一階段:Prepare階段
A把申請修改的請求Prepare Request發給全部的結點A,B,C。注意,Paxos算法會有一個Sequence Number(你能夠認爲是一個提案號,這個數不斷遞增,並且是惟一的,也就是說A和B不可能有相同的提案號),這個提案號會和修改請求一同發出,任何結點在「Prepare階段」時都會拒絕其值小於當前提案號的請求。因此,結點A在向全部結點申請修改請求的時候,須要帶一個提案號,越新的提案,這個提案號就越是是最大的。
若是接收結點收到的提案號n大於其它結點發過來的提案號,這個結點會迴應Yes(本結點上最新的被批准提案號),並保證不接收其它<n的提案。這樣一來,結點上在Prepare階段里老是會對最新的提案作承諾。
優化:在上述 prepare 過程當中,若是任何一個結點發現存在一個更高編號的提案,則須要通知 提案人,提醒其中斷此次提案。
第二階段:Accept階段
若是提案者A收到了超過半數的結點返回的Yes,而後他就會向全部的結點發布Accept Request(一樣,須要帶上提案號n),若是沒有超過半數的話,那就返回失敗。
當結點們收到了Accept Request後,若是對於接收的結點來講,n是最大的了,那麼,它就會修改這個值,若是發現本身有一個更大的提案號,那麼,結點就會拒絕修改。
咱們能夠看以,這彷佛就是一個「兩段提交」的優化。其實,2PC/3PC都是分佈式一致性算法的殘次版本,Google Chubby的做者Mike Burrows說過這個世界上只有一種一致性算法,那就是Paxos,其它的算法都是殘次品。
咱們還能夠看到:對於同一個值的在不一樣結點的修改提案就算是在接收方被亂序收到也是沒有問題的。
關於一些實例,你能夠看一下Wikipedia中文中的「Paxos樣例」一節,我在這裏就再也不多說了。對於Paxos算法中的一些異常示例,你們能夠本身推導一下。你會發現基本上來講只要保證有半數以上的結點存活,就沒有什麼問題。
多說一下,自從Lamport在1998年發表Paxos算法後,對Paxos的各類改進工做就從未中止,其中動做最大的莫過於2005年發表的Fast Paxos。不管何種改進,其重點依然是在消息延遲與性能、吞吐量之間做出各類權衡。爲了容易地從概念上區分兩者,稱前者Classic Paxos,改進後的後者爲Fast Paxos。
下圖來自:Google App Engine的co-founder Ryan Barrett在2009年的google i/o上的演講《Transaction Across DataCenter》(視頻: http://www.youtube.com/watch?v=srOgpXECblk)
前面,咱們說過,要想讓數據有高可用性,就須要冗餘數據寫多份。寫多份的問題會帶來一致性的問題,而一致性的問題又會帶來性能問題。從上圖咱們能夠看到,咱們基本上來講不可讓全部的項都綠起來,這就是著名的CAP理論:一致性,可用性,分區容忍性,你只可能要其中的兩個。
最後我還想提一下Amazon Dynamo的NWR模型。這個NWR模型把CAP的選擇權交給了用戶,讓用戶本身的選擇你的CAP中的哪兩個。
所謂NWR模型。N表明N個備份,W表明要寫入至少W份才認爲成功,R表示至少讀取R個備份。配置的時候要求W+R > N。 由於W+R > N, 因此 R > N-W 這個是什麼意思呢?就是讀取的份數必定要比總備份數減去確保寫成功的倍數的差值要大。
也就是說,每次讀取,都至少讀取到一個最新的版本。從而不會讀到一份舊數據。當咱們須要高可寫的環境的時候,咱們能夠配置W = 1 若是N=3 那麼R = 3。 這個時候只要寫任何節點成功就認爲成功,可是讀的時候必須從全部的節點都讀出數據。若是咱們要求讀的高效率,咱們能夠配置 W=N R=1。這個時候任何一個節點讀成功就認爲成功,可是寫的時候必須寫全部三個節點成功才認爲成功。
NWR模型的一些設置會形成髒數據的問題,由於這很明顯不是像Paxos同樣是一個強一致的東西,因此,可能每次的讀寫操做都不在同一個結點上,因而會出現一些結點上的數據並非最新版本,但卻進行了最新的操做。
因此,Amazon Dynamo引了數據版本的設計。也就是說,若是你讀出來數據的版本是v1,當你計算完成後要回填數據後,卻發現數據的版本號已經被人更新成了v2,那麼服務器就會拒絕你。版本這個事就像「樂觀鎖」同樣。
可是,對於分佈式和NWR模型來講,版本也會有惡夢的時候——就是版本衝的問題,好比:咱們設置了N=3 W=1,若是A結點上接受了一個值,版本由v1 -> v2,但尚未來得及同步到結點B上(異步的,應該W=1,寫一份就算成功),B結點上仍是v1版本,此時,B結點接到寫請求,按道理來講,他須要拒絕掉,可是他一方面並不知作別的結點已經被更新到v2,另外一方面他也沒法拒絕,由於W=1,因此寫一分就成功了。因而,出現了嚴重的版本衝突。
Amazon的Dynamo把版本衝突這個問題巧妙地迴避掉了——版本衝這個事交給用戶本身來處理。
因而,Dynamo引入了Vector Clock(矢量鍾?!)這個設計。這個設計讓每一個結點各自記錄本身的版本信息,也就是說,對於同一個數據,須要記錄兩個事:1)誰更新的我,2)個人版本號是什麼。
下面,咱們來看一個操做序列:
1)一個寫請求,第一次被節點A處理了。節點A會增長一個版本信息(A,1)。咱們把這個時候的數據記作D1(A,1)。 而後另一個對一樣key的請求仍是被A處理了因而有D2(A,2)。這個時候,D2是能夠覆蓋D1的,不會有衝突產生。
2)如今咱們假設D2傳播到了全部節點(B和C),B和C收到的數據不是從客戶產生的,而是別人複製給他們的,因此他們不產生新的版本信息,因此如今B和C所持有的數據仍是D2(A,2)。因而A,B,C上的數據及其版本號都是同樣的。
3)若是咱們有一個新的寫請求到了B結點上,因而B結點生成數據D3(A,2; B,1),意思是:數據D全局版本號爲3,A升了兩新,B升了一次。這不就是所謂的代碼版本的log麼?
4)若是D3沒有傳播到C的時候又一個請求被C處理了,因而,以C結點上的數據是D4(A,2; C,1)。
5)好,最精彩的事情來了:若是這個時候來了一個讀請求,咱們要記得,咱們的W=1 那麼R=N=3,因此R會從全部三個節點上讀,此時,他會讀到三個版本:
6)這個時候能夠判斷出,D2已是舊版本(已經包含在D3/D4中),能夠捨棄。
7)可是D3和D4是明顯的版本衝突。因而,交給調用方本身去作版本衝突處理。就像源代碼版本管理同樣。
很明顯,上述的Dynamo的配置用的是CAP裏的A和P。
我很是推你們都去看看這篇論文:《Dynamo:Amazon’s Highly Available Key-Value Store》,若是英文痛苦,你能夠看看譯文(譯者不詳)。