深入理解分散式系統:分割槽、複製、分散式事務以及系統一致性與共識

xuxh120發表於2021-12-01

分散式資料

引言

你可能會出於各種各樣的原因,希望將資料庫分佈到多臺機器上:

可擴充套件性
如果你的資料量、讀取負載、寫⼊負載超出單臺機器的處理能⼒,可以將負載分散到多臺計算機上。

容錯/⾼可⽤性
如果你的應⽤需要在單臺機器(或多臺機器,⽹絡或整個資料中⼼)出現故障的情況下仍然能繼續⼯
作,則可使⽤多臺機器,以提供冗餘。⼀臺故障時,另⼀臺可以接管。

延遲
如果在世界各地都有⽤戶,你也許會考慮在全球範圍部署多個伺服器,從⽽每個⽤戶可以從地理上最近
的資料中⼼獲取服務,避免了等待⽹絡資料包穿越半個世界。

在具體實現上,⽆共享架構(shared-nothing architecture)因其較高的價效比,以及強大的功能而被廣泛使用,無共享架構有時稱為⽔平擴充套件(horizontal scale) 或向外擴充套件(scale out))。
在這種架構中,運⾏資料庫軟體的每臺機器/虛擬機器都稱為節點(node)。每個節點只使⽤各⾃的處理器,記憶體和磁碟。節點之間的任何協調,都是在軟體層⾯使⽤傳統⽹絡實現的。

雖然分散式⽆共享架構有許多優點,但它通常也會給應⽤帶來額外的複雜度,有時也會限制你可⽤資料模型的表達⼒。接下來我們將詳細討論分散式系統帶來的各種問題,以及問題的解決方案;

五.複製

  • 適用場景:少資料量,單庫可承載的場景。

  • 目的:

    • 1.擴充套件性。提高吞吐量
    • 2.降低延遲。通過將資料放在離使用者較近的地方,以便能夠更快的訪問資料。
    • 3.高可用。即使部分節點故障停機,也能保持服務正常執行。
  • 領導者&追隨者

    • I.同步複製 vs 非同步複製

      • a.同步複製

        • 優點

          • 從庫保證有與主庫⼀致的最新資料副本。如果主庫突然失效,我們可以確信這些資料仍然能在從庫上上找到。
        • 缺點

          • 如果同步從庫沒有響應(⽐如崩潰,或其它任何原因),主庫就⽆法處理寫⼊操作。
        • 將所有從庫都設定為同步的是不切實際的:任何⼀個節點的中斷都會導致整個系統停滯不前。

      • b.非同步複製

        • 優點

          • 即使所有的從庫都落後了,主庫也可以繼續處理
            寫⼊。
        • 缺點

          • 會出現複製延遲問題,見下文。
        • 通常情況下,基於領導者的複製都配置為完全非同步。

      • c.半同步

        • 只保證一部分節點是保持與主庫一致性的資料。
    • II.設定新從庫

      • 步驟:

        • 1.獲取主庫某時刻一致性快照。
          2.快照複製
          3.連結主庫,拉取快照後的資料變更。要求主庫中日誌位置精確,如日誌序列號,二進位制日誌座標。
          4.從庫趕上主庫後,就可以正常處理主庫的產生資料變化了。
    • III.處理節點當機

      • 從庫失效:追趕恢復
      • 主庫失效:故障切換
    • IV.複製日誌的實現

      • 定義:每當領導者將新資料寫⼊本地儲存時,它
        也會將資料變更傳送給所有的追隨者,稱之為複製⽇志(replication log)記錄或變更流
        (change stream)。

      • 實現1.基於語句的複製

        • 優點:可讀性較強
          缺點:執行依賴於語句的複製會有問題,如:自增主鍵、隨機字串、now()函式,儲存過程等。
        • 實踐:mysql預設使用該模式,並在特定情況下切換至基於日誌的複製模式。
      • 實現2.傳輸預寫式日誌WAL(Write Ahead Log)

        • 優點:基於追加日誌的方式,記錄資料的變更,可以解決以上覆制問題。
          缺點:1.wal偏底層,通常記錄的是磁碟某分割槽資料更改;2.資料與儲存耦合性較強;
        • 實踐:PostgreSQL和oracle使用此複製方式。
      • 實現3.基於邏輯日誌(Binlog)的複製

        • 優點:資料變更的二進位制位元組流,傳輸速度快,且保證資料複製的正確型。
          缺點:不可讀。
        • 實踐:mysql的二進位制日誌使用該方式
      • 實現4.基於觸發器的複製

        • 使用應⽤程式程式碼複製
  • 複製延遲問題

    • 最終一致性:同時對主庫和從庫執⾏相同的查詢,可能得到不同的結果,因為並⾮所有的寫⼊都反映在從庫中。如果停⽌寫⼊資料庫並等待⼀段時間,從庫最終會趕上並與主庫保持⼀致,這種效應成為最終一致性。
    • 1.寫後讀:使用者總是可以讀到自己提交的資料。
    • 2.單調性:即使用者在讀取某個時間的資料後,不應再讀取到更早時間點的資料。
    • 3.一致字首讀:使用者應該將資料視為具有因果意義的狀態。例如:按照正確的順序檢視問題及其答覆。
  • 三種流行的複製演算法

    • 1.單主複製

      • 客戶端將所有的寫操作傳送到單個節點(領導者),該節點將資料更改事件流傳送到所有的副本(追隨者)。讀操作可以在任何節點執行,但可能會讀取到舊資料。
      • 優點:容易理解,且沒有衝突問題。
    • 2.多主複製

      • 客戶端的將寫操作傳送到幾個領導者節點之一,其中任何一個都可以接受寫入。然後由該節點將資料更改事件流傳送給其他領導者節點及其跟隨者節點。
      • 優點:出現故障節點、網路中斷和延遲峰值情況下,多主和無主複製更加穩健。但以僅提供弱一致性為代價。
      • 併發問題
    • 3.無主複製

      • 客戶端傳送每個寫入到多個節點,並從多個節點並行讀取,以檢測和糾正具有陳舊資料的節點。
      • 併發問題

六.分割槽

  • 適用場景:資料量⾮常⼤的時候,在單臺機器上
    儲存和處理不再可⾏,則分割槽⼗分必要。
    此時需要將資料進⾏分割槽(partitions),也稱為分⽚(sharding) 。

  • 目的:分割槽的⽬標是在多臺機器上均勻分佈資料和查詢負載,避免出現熱點(負載不成⽐例的節點)。

  • 分割槽與複製:

    • 分割槽通常與複製結合使⽤,使得每個分割槽的副本儲存在多個節點上。 這意味著,即使每條記錄屬於⼀個分割槽,它仍然可以儲存在多個不同的節點上以獲得容錯能⼒。
  • 兩種主要的分割槽⽅法:

    • 鍵範圍分割槽

      • 核心思想:為每個分割槽指定⼀塊連續的鍵範圍(從最⼩值到最⼤值),如紙百科全書的卷)。如果知道範圍之間的邊界,則可以輕鬆確定哪個分割槽包含某個值。
      • 優點:鍵是有序的,並且分割槽擁有從某個最⼩值到某個最⼤值的所有鍵。可以進⾏有效的範圍查詢。
      • 缺點:如果應⽤程式經常訪問相鄰的主鍵,則存在熱點和偏斜的⻛險。
    • 雜湊分割槽

      • 核心思想:雜湊進⾏分割槽,通常先提前建立固定數量的分割槽,為每個節點分配多個分割槽,並在新增或刪除節點時將整個分割槽從⼀個節點移動到另⼀個節點。也可以使⽤動態分割槽。
      • 優點:可以將將偏斜的資料均勻分佈
      • 缺點:雜湊分割槽破壞了鍵的排序,使得範圍查詢效率低下
    • 鍵範圍&雜湊組合分割槽

      • 兩種⽅法搭配使⽤也是可⾏的,例如使⽤複合主鍵:使⽤鍵的⼀部分來標識分割槽,⽽使⽤另⼀部分作為排序順序。
  • 分割槽與二級索引:分割槽和⼆級索引之間的相互作⽤。二級索引也需要分割槽,有兩種分割槽⽅法:

    • 方法1: 按⽂檔分割槽(本地索引)。特點:
      1.⼆級索引儲存在與主鍵和值相同的分割槽中。
      2.這意味著只有⼀個分割槽需要在寫⼊時更新,
      3.但是讀取⼆級索引需要在所有分割槽之間進⾏分散/收集。
    • 方法2: 按關鍵詞Term分割槽(全域性索引)。
      特點:
  1. ⼆級索引存在不同的分割槽。輔助索引中的條⽬可以包括來⾃主鍵的所有分割槽的記錄。
    2.當⽂檔寫⼊時,需要更新多個分割槽中的⼆級索引;
    3.但是可以從單個分割槽中進⾏讀取。
  • 分割槽再平衡

    • 定義:隨著時間的推移,資料庫會有各種變化。如查詢吞吐量增加、資料集大小增加、節點機器故障下線等,此時需要資料和請求從⼀個節點移動到另⼀個節點。 將負載從叢集中的⼀個節點向另⼀個節點移動的過程稱為再平衡(reblancing)。

    • 平衡策略

      • 反面教材:hash mod N

        • \(N\)⽅法的問題是,如果節點數量N發⽣變化,⼤多數金鑰將需要從⼀個節點移動到另⼀個節點。使得重新平衡過於昂貴。我們需要⼀種只移動必需資料的⽅法。
      • 固定數量的分割槽

        • ⼀種只移動必需資料的簡單⽅法:建立⽐節點更多的分割槽,併為每個節點分配多個分割槽。只有分割槽在節點之間的移動。分割槽的數量不會改變,鍵所指定的分割槽也不會改變。唯⼀改變的是分割槽所
          在的節點。
        • 如果分割槽⾮常⼤,再平衡和從節點故障恢復變得昂貴。如果分割槽太⼩,則會產⽣太多的開銷。
          當分割槽⼤⼩“恰到好處”的時候才能獲得很好的效能,如果分割槽數量固定,但資料量變動很⼤,則難以達到最佳效能。
      • 動態分割槽

        • 每個分割槽分配給⼀個節點,每個節點可以處理多個分割槽,就像固定數量的分割槽⼀樣。當分割槽增⻓到超過配置的⼤⼩時,拆分⼤型分割槽將其中的⼀半轉移到另⼀個節點,以平衡負載;與之相反,如果⼤量資料被刪除並且分割槽縮⼩到某個閾值以下,則可以將其與相鄰分割槽合併。此過程與B樹頂層發⽣的過程類似(參閱“B樹”)。
        • 動態分割槽的⼀個優點是分割槽數量適應總資料量。如果只有少量的資料,少量的分割槽就⾜夠了,所以開銷很⼩;如果有⼤量的資料,每個分割槽的⼤⼩被限制在⼀個可配置的最⼤值【23】
        • 動態分割槽不僅適⽤於資料的範圍分割槽,⽽且也適⽤於雜湊分割槽。從版本2.4開始,MongoDB同時⽀持範圍和雜湊分割槽,並且都是進⾏動態分割分割槽。
      • 按節點⽐例分割槽

        • Cassandra和Ketama使⽤的第三種⽅法是使分割槽數與節點數成正⽐——換句話說,每個節點具有固定數量的分割槽【23,27,28】。在這種情況下,每個分割槽的⼤⼩與資料集⼤⼩成⽐例地增⻓,⽽節點數量保持不變,但是當增加節點數時,分割槽將再次變⼩。由於較⼤的資料量通常需要較⼤數量的節點進⾏儲存,因此這種⽅法也使每個分割槽的⼤⼩較為穩定。
  • 請求路由

    • 分割槽負載平衡
    • 並⾏查詢執⾏引

七、事務

  • 事務是⼀個抽象層,允許應⽤程式假裝某些併發問題和某些型別的硬體和軟體故障不存在。各式各樣的錯誤被簡化為⼀種簡單情況:事務中⽌(transaction abort),⽽應⽤需要的僅僅是重試。

  • 弱隔離級別

    • 1.讀已提交Read Commit

      • 兩個保證:

        • 無髒讀
        • 無髒寫
      • 實現讀已提交

        • 防止髒寫

          • 使用行鎖實現:當事務想要修改特定的物件(行或文件)時,它必須首先獲得該物件的鎖。然後必須持有該鎖直到事務提交或中止;
            一次只有一個事務可以持有任何給定物件的鎖;
            如果另一事務想要寫入同一個物件,則必須等到第一個事務提交或中止後才能獲取該鎖並繼續。
        • 防止髒讀

          • 方法一:使用行鎖。但實踐效果並不好,損失了只讀事務的響應時間,並且可能因為等待鎖導致連鎖反應從而使整體響應遲緩。
          • 方法二:對於寫入的每個物件,資料庫都會記住舊的已經提交的值,和由當前持有寫入鎖的事務設定的新值;
            當事務正在進行時,任何其他讀取物件的事務都會拿到舊值;
            只有當新值提交以後,事務才會切換到讀取新值。
      • 不足:

        • 不可重複讀 nonrepeatable 或讀取偏差 read skew
        • 丟失更新Lost update
    • 2.快照隔離和可重複讀Read Repeatable

      • 一個保證:

        • 可重複讀
      • 不可重複讀問題

        • 描述:事務A在對一個物件寫入期間,另一個事務B分別在事務A提交前、後讀取到不同值的現象。

        • 解決

          • 快照隔離:每個事務都從資料庫的一致快照(consistent snapshot)中讀取,即事務可以看到事務開始時在資料庫中提交的所有資料,即使這些資料隨後被另一個事務更改,每個事務也只能看到該特定時間點的舊資料。
          • 快照隔離對長時間執行只讀查詢非常有用,如備份和分析。
      • 實現快照隔離

        • 關鍵原則:讀不阻塞寫,寫不阻塞讀。

        • 多版本併發控制MVCC,multi-version concurrency control:資料庫保留和維護同一個物件在不同時間點的多個提交版本。

        • PostSQL中MVCC的實現:

          • 當一個事務開始時會被賦予一個唯一的永遠遞增的事務ID,每當事務向資料庫寫入任何內容時,它所寫入的資料都會被標記上寫入者的事務ID。
          • 可見性規則:同時滿足以下2個條件,則可見一個物件:
            a。讀事務開始時,建立該物件的事務已經提交。
            b。物件未被標記刪除或已被標記刪除,請求刪除的事務在讀事務開始時尚未提交。
    • 3.防止丟失更新Lost update

      • 併發寫入事務可能導致的問題

      • 解決方案

        • a。原子寫

          • 許多資料庫提供了原⼦更新操作,從⽽消除了在應⽤程式程式碼中執⾏讀取-修改-寫⼊序列的需要。如下在大多數資料庫是併發安全的:
            UPDATE counters SET value = value + 1 WHERE key = 'foo';
            如果你的程式碼需要,那這通常是最好的解決⽅案。

          • a。實現

            • 方案一:遊標穩定性技術,事務在讀取物件時獲取其上的排他鎖,在更新操作完成之前沒有其他事務可以讀取該物件。通常的實現方式。
            • 方案二:簡單地強制所有的原子操作在單一執行緒上執行。
        • b。顯示鎖定

          • 防⽌丟失更新的另⼀個選擇是讓應⽤程式顯式地鎖定將要更新的物件。然後應⽤程式可以執⾏讀取-修改-寫⼊序列,如果任何其他事務嘗試同時讀取同⼀個物件,則強制等待,直到第⼀個讀取-修改-寫⼊序列完成。
          • FOR UPDATE ⼦句告訴資料庫應該對該查詢返回的所有⾏加鎖。
        • c。自動檢測丟失的更新

          • 原⼦操作和鎖是通過強制讀取-修改-寫⼊序列按順序發⽣,來防⽌丟失更新的⽅法。

          • 另⼀種⽅法是允許它們並⾏執⾏,如果事務管理器檢測到丟失更新,則中⽌事務並強制它們重試其讀取-修改-寫⼊序列。

          • 優點:資料庫可以結合快照隔離⾼效地執⾏此檢查。

            • Oracle可序列化和SQL server快照隔離級別都會自動檢測丟失的更新。
            • MySQL/InnoDB的可重複讀並不會檢測丟失更新。
        • d。比較並設定(CAS, Compare And Set)

          • 此操作的⽬的是為了避免丟失更新。
          • 但是,如果資料庫允許 WHERE ⼦句從舊快照中讀取,則此語句可能⽆法防⽌丟失更新(MVCC)
          • 在依賴資料庫的CAS操作前要檢查其是否安
            全。
        • e。衝突解決和複製

          • 多節點情況

            • 防⽌丟失的更新需要考慮另⼀個維度:由於在多個節點上存在資料副本,並且在不同節點上的資料可能被併發地修改,基於鎖或CAS操作的技術不適⽤於這種情況,因此需要採取⼀些額外的步驟來防⽌丟失更新。
            • ⼀種常⻅⽅法是允許併發寫⼊建立多個衝突版
              本的值(也稱為兄弟),並使⽤應⽤程式碼或特殊資料結構在事實發⽣之後解決和合並這些版本。
            • 另⼀⽅⾯,最後寫⼊為準(LWW)也可解決衝突,但該⽅法很容易丟失更新。
        • f。寫入偏差和幻讀

          • I.不同事務併發寫入相同物件情況

            • 導致的問題

              • 髒寫,丟失更新
            • 解決方式

              • 通過【鎖】和【原子寫操作】這類手動安全措施。
          • II.不同事務併發寫入不同物件情況

            • 導致的問題

              • 寫偏差,幻讀
            • 寫偏差

              • 如果兩個事務讀取相同的物件,然後更新其中⼀些物件(不同的事務可能更新不同的物件),則可能發⽣寫⼊偏差。
            • 導致寫偏差的幻讀

              • ⼀個事務中的寫⼊改變另⼀個事務的搜尋查詢的結果,被稱為幻讀【3】
            • 解決方式

              • 較優:使用觸發器,或者物化檢視
              • 次優:使用FOR UPDATE顯示鎖定事務所依賴的所有行。
            • 物化衝突

              • 如果幻讀的問題是沒有物件可以加鎖,也許可以⼈為地在資料庫中引⼊⼀個鎖物件?
              • 例如,在會議室預訂的場景中,可以想象建立⼀個關於時間槽和房間的表。此表中的每⼀⾏對應於特定時間段(例如15分鐘)的特定房間。可以提前插⼊房間和時間的所有可能組合⾏(如接下來的六個⽉)。
                現在,要建立預訂的事務可以鎖定( SELECT FOR UPDATE )表中與所需房間和時間段對應的⾏。在獲得鎖定之後,它可以檢查重疊的預訂並像以前⼀樣插⼊新的預訂。
                請注意,這個表並不是⽤來儲存預訂相關的資訊——它完全就是⼀組鎖,⽤於防⽌同時修改同⼀房間和時間範圍內的預訂。
              • 這種⽅法被稱為物化衝突(materializing conflicts),因為它將幻讀變為資料庫中⼀組具體⾏上的鎖衝突【11】。
                不幸的是,弄清楚如何物化衝突可能很難,也很容易出錯。在⼤多數情況下。可序列化 的隔離級別是更可取的方案。
  • 4.可序列化(Serializable)

    • 目標:解決幻讀

    • 實現

      • 方案一:真正的序列化

        • 順序執⾏所有事務使併發控制簡單多了,但資料庫的事務吞吐量被限制為單機單核的速度。
          只讀事務可以使⽤快照隔離在其它地⽅執⾏,但對於寫⼊吞吐量較⾼的應⽤,單執行緒事務處理器可能成為⼀個嚴重的瓶頸。

        • 最佳實踐

          • 1.每個事務都必須小而快,只要有一個緩慢的事務,就會拖慢所有事務處理。
          • II。僅限於活躍資料集可以放入記憶體的情況,因為訪問磁碟會很慢。
          • III。寫入吞吐量必須低到能在單個CPU核上處理,否則事務需要化分支單個分割槽,且不需要跨分割槽協調。
          • IV。跨分割槽事務是可能的,但是他們的使用成都有很大的限制。
        • 分割槽

          • 為了擴充套件到多個CPU核⼼和多個節點,可以對資料進⾏分割槽。
          • 找到⼀種對資料集進⾏分割槽的⽅法,以便每個事務只需要在單個分割槽中讀寫資料,那麼每個
            分割槽就可以擁有⾃⼰獨⽴運⾏的事務處理執行緒。在這種情況下可以為每個分割槽指派⼀個獨⽴的CPU核,事務吞吐量就可以與CPU核數保持線性擴充套件【47】。
      • 方案二:兩階段鎖定(2PL)

        • 實現

        • 讀阻塞寫,寫阻塞讀。表級別

        • 悲觀鎖

        • 效能如何

          • 效能非常差
        • 變形一:謂詞鎖

          • 它類似於2PL描述的共享/排它鎖,但不屬於特定的物件(例如,表中的⼀⾏),它屬於所有符合某些搜尋條件的物件。
          • 讀阻塞寫,寫阻塞讀。條件匹配的資料行級別
          • 效能較差
        • 變形二:索引範圍鎖

          • 又叫間隙鎖next-key locking,大多數2PL資料的實現。近似版的謂詞鎖
          • 這種方法可能會鎖定更大範圍的物件,而不是維持可串性化所必須的範圍。
            它可以有效防止幻讀和寫入偏差,開銷也較低,是一個很好的折中選擇。
          • 讀阻塞寫,寫阻塞讀。條件中索引列的級別,如果無索引則是表級別
      • 方案三:可序列化快照隔離(SSI,)

        • 序列化的隔離級別和⾼效能是從根本上相互⽭盾的嗎?可序列化隔離提供了一種選擇,它提供了完整的可序列化隔離級別,但與快照隔離相⽐只有只有很⼩的效能損失。

        • 樂觀鎖:即如果存在潛在的危險也不阻止事務,而是繼續執行事務,希望一起都好起來。當一個事務想要提交時,資料庫檢查是否有什麼不好的事情發生(即隔離是否被違反);如果是,事務將被中止,並且必須重拾。只有可序列化的事務才被允許提交。

        • 樂觀鎖的優點和缺點:

          • 如果存在很多爭用/競爭,則表現不佳,因為這會導致很大一部分事務需要中止。如果系統已經接近最大吞吐量,來自重試事務的額外負載可能會使效能變差。
          • 但是如果有足夠的備用容量,並且事務之間的爭用不是太高,樂觀的併發控制技術往往比悲觀的效能要好。
        • 顧名思義SSI基於快照隔離,也就是事務中所有讀取都是來自資料庫的一致性快照。在快照隔離的基礎上,SSI增加了一種演算法來檢測寫入之間的序列化衝突,並確定要中止哪些事務。

        • 效能

          • 中⽌率顯著影響SSI的整體表現。SSI要求同時讀寫的事務儘量短(只讀⻓事務可能沒問題)。此時效能較好

八.分散式系統的麻煩

  • 子主題 4

    • 時鐘錯誤
    • 程式暫停
    • 無上限的網路延遲
  • 故障與部分失效

    • 單個計算機上的軟體,通常會以⼀種相當可預測的⽅式運⾏,它沒有根本性的不可靠原因。
      在分散式系統中,情況有本質上的區別。在分散式系統中,儘管系統的其他部分⼯作正常,但系統的某些部分可能會以某種不可預知的⽅式被破
      壞。這被稱為部分失效(partial failure)。
    • 難點在於部分失效是不確定性的
      (nonderterministic):如果你試圖做任何涉及多個節點和⽹絡的事情,它有時可能會⼯作,有時會出現不可預知的失敗。
    • 如果要使分散式系統⼯作,就必須接受部分故障的可能性,並在軟體中建⽴容錯機制。換句話說,我們需要從不可靠的元件構建⼀個可靠的系統。
      故障處理必須是軟體設計的⼀部分,並且作為軟體的運維,您需要知道在發⽣故障的情況下,軟體可能會表現出怎樣的⾏為。
  • 不可靠的網路

    • 真實的網路環境很不穩定,⽹絡故障時有發生,如果⽹絡故障的錯誤處理沒有定義與測試,武斷地講,各種錯誤可能都會發⽣。
      您需要知道您的軟體如何應對⽹絡問題,並確保系統能夠從中恢復。
      有意識地觸發⽹絡問題並測試系統響應。

    • 檢測故障

      • 如果你想確保⼀個請求是成功的,你需要應⽤程式本身的積極響應【24】。
    • 超時與⽆窮的延遲

      • 如果超時是檢測故障的唯⼀可靠⽅法,那麼超時應該等待多久?不幸的是沒有簡單的答案。

        • ⽹絡擁塞和排隊
      • ⻓時間的超時意味著⻓時間等待,直到⼀個節點被宣告死亡。在這段時間內⽤戶可能不得不等待或者看到錯誤資訊。
        短暫的超時可以更快地檢測到故障,但是實際上它只是經歷了暫時的減速⽽導致錯誤地宣佈節點失效的⻛險更⾼。例如,由於節點或⽹絡上的負載峰值。

      • 更好的⼀種做法是,系統不是使⽤配置的常量超時,⽽是連續測量響應時間及其變化(抖動),並根據觀察到的響應時間分佈⾃動調整超時。

    • 同步網路vs非同步網路

      • 電話電路

        • 使用固定的網路頻寬,傳輸的資料量固定、可預測。可保證的最⼤往返時間。
        • 低請求時浪費頻寬,超過可支援頻寬的高請求時網路阻塞。
      • 分組交換協議

        • 根據網路中資料型別及大小的不同,傳輸的資料量也是不可預測的,是動態變化的。
        • 有利於應對突發流量的情況。
  • 不可靠的時鐘

    • 時鐘和時間很重要!因為很多業務場景都依賴於時鐘或時間

    • 分散式系統中,時間是一個比較棘手的事情:
      1.因為網路的傳輸需要時間,故節點間通訊是不及時的。
      2.每臺節點機器都有自己的時鐘,他們不是完全準確的。一組伺服器通常使用“網路時間協議NTP”在一定程度上同步時鐘。

    • 單調鍾和時鐘

      • 現代計算機中至少有兩種不同的時鐘:即時鐘和單調鍾。

      • 時鐘

        • 也成為掛鐘時間wall-clock time,他根據某個日曆返回當前的日期和時間。如:
          Linux上的clock_gettime(CLOCK_REALTIME)和
          Java中的System.currentTimemillis()返回epoch(即一個特定的時間:1970年1月1日午夜UTC)以來的秒數或毫秒數,不包括閏秒。
        • 時鐘通常與NTP同步,如果本地使用在NTP伺服器之前太遠,則他可能會被重置到先前的時間點,發生時鐘跳回。
        • 因為時鐘跳回和忽略閏秒,使用不用用於測量經過的時間。可以作為一個時間日期的參考值。
      • 單調鍾

        • 單調鍾永續測量持續時間,即間隔時間,這個名字來源於單調鍾保證前進的,而不會像時鐘一樣跳回。如:
          Linux上clock_gettime(CLOCK_MONOTONIC) ,和Java中的 System.nanoTime() 都是單調時鐘。

          • 在具有多個CPU插槽的伺服器上,每個CPU可能有⼀個單獨的計時器,但不⼀定與其他CPU同步。明智的做法是不要太把這種單調性保證當回事
        • 單調鐘的絕對值是毫無意義的,因為他可能是任何值。

        • 單調鐘的解析度相當好:大多數系統中,他們能在幾微秒或更短時間內測量時間間隔。

        • 單調鐘不需要同步。同一機器的不同CPU間,分散式系統的不同節點間。。等

      • 時鐘的不準確性:單調鐘不需要同步,但是時鐘需要根據NTP伺服器或其他外部時間源來設定才能有⽤。但計算機中的⽯英鐘不夠精確:它會漂移(drifts)(運⾏速度快於或慢於預期)。時鐘漂移取決於機器的溫度。
        使⽤GPS接收機,精確時間協議(PTP)【52】以及仔細的部署和監測可以實現這種精確度。

      • 暫停程式?

        • GC暫停
        • 虛擬機器掛起
        • 長時間的I/O操作
        • 。。。
      • 代價:如果某個軟體依賴於精確同步的時鐘,那麼結果更可能是悄⽆聲息且⾏蹤渺茫資料的資料丟失,⽽不是⼀次驚天動地的崩潰【53,54】。

  • 知識、真相與謊言

    • 真理由多數所定義

      • a。分散式系統不能完全依賴於單個節點,因為節點回會是失效,可能會使系統卡死,無法恢復。相反,許多分散式演算法都依賴於【法定人數】,即在多個節點之間投票決定減少對某個節點的依賴。
        也包括宣告節點死亡的決定,即使一個節點仍然感覺到自己活著,他也必須認為是死的(錯誤的宣告死亡)。個體節點必須遵守法定決定並下臺。
      • b。通常情況下,⼀些東⻄在⼀個系統中只能有⼀個。如單主複製中的領導者節點、鎖等。
        如果⼀個節點繼續表現為“天選者”,即使⼤多數節點已經宣告它已經死了,則在考慮不周的系統中可能會導致問題。

防護令牌:當使⽤鎖或租約來保護對某些資源的訪問時,需要確保⼀個被誤認為⾃⼰是“天選者”的節點不能中斷系統的其它部分。實現這⼀⽬標的⼀個相當簡單的技術就是防護令牌。(fencing)遮蔽令牌保證它是單調遞增,資源僅接受最新的寫入。
- c。請注意,這種機制要求資源本身在檢查令牌⽅⾯發揮積極作⽤,通過拒絕使⽤舊的令牌,⽽不是已經被處理的令牌來進⾏寫操作——僅僅依靠客戶端檢查⾃⼰的鎖狀態是不夠的。

- 拜占庭故障

	- 拜占庭故障:在不信任的環境中達成共識的問題被稱為拜占庭將軍問題。

	- 拜占庭容錯

		- 拜占庭容錯:當⼀個系統在部分節點發⽣故障、不遵守協議、甚⾄惡意攻擊、擾亂⽹絡時仍然能繼續正確⼯作,稱之為拜占庭容錯(Byzantine fault-tolerant)
		- 拜占庭容錯相當複雜&實現成本很高:在本書討論的那些系統中,我們通常可以安全地假設沒有拜占庭式的錯誤。在你的資料中⼼⾥,

所有的節點都是由你的組織控制的(所以他們可以信任),輻射⽔平⾜夠低,記憶體損壞不是⼀個⼤問題。
製作拜占庭容錯系統的協議相當複雜【84】,部署拜占庭容錯解決⽅案的成本使其變得不切實際。

	- 弱謊言形式

		- 弱謊言形式提供簡單實用的可靠性保證:儘管我們假設節點通常是誠實的,但值得向軟體中新增防⽌“撒謊”弱形式的機制——例如,由硬體問題導致的⽆效訊息,軟體錯誤和錯誤配置。這種保護機制並不是完全的拜占庭容錯,但它們仍然是簡單⽽實⽤的步驟,以提⾼可靠性。

- 系統模型與實現

	- 演算法

		- 有很多演算法被設計以解決分散式系統問題,即容忍分散式系統的各種故障。

	- 系統模型:這個模型是⼀個抽象,描述⼀個

【演算法】可能承擔的事情。以某種方式將我們期望在系統中發生的錯誤形式化。

		- 定時假說系統模型

			- a。同步模型:假設⽹絡延遲,程式暫停和和時鐘誤差都是有界限的,即假設⽹絡延遲,暫停和時鐘漂移將永遠不會超過某個固定的上限。需要注意⽆限延遲的實際情況。
			- b。部分同步模型:⼀個系統在⼤多數情況下像⼀個同步系統⼀樣運⾏,但有時候會超出⽹絡延遲,程式暫停和時鐘漂移的界限。
			- c。非同步模型:不允許對時機做任何假設。

		- 節點失效系統模型

			- a。崩潰-停⽌故障(crash-stop):意味著節點可能在任意時刻突然停⽌響應,此後該節點永遠消失——它永遠不會回來
			- b。崩潰-恢復故障(crashrecovery):假設節點可能會在任何時候崩潰,但也許會在未知的時間之後再次開始響應。節點具有穩定的儲存且會在崩潰中保留,⽽記憶體中的狀態會丟失。
			- c。拜占庭(任意)故障:節點可以做(絕對意義上的)任何事情,包括試圖戲弄和欺騙其他節點。
			- 對於真實系統的建模,具有崩潰-恢復故障(crash-recovery)的部分同步模型(partialsynchronous)通常是最有⽤的模型。

	- 演算法的正確性

九.一致性與共識

  • 一致性保證

    • 最終一致性:如果你在同⼀時刻問兩個不同副本相同的問題,可能會得到兩個不同的答案。但如果停止向資料庫寫入資料並等待一段不確定時間,那麼最終讀取請求會得到相同的答案。
    • 分散式⼀致性模型和我們之前討論的事務隔離級別的層次結構有⼀些相似之處。但它們⼤多是⽆關的問題:事務隔離主要是為了,避免由於【同時執⾏事務⽽導致的競爭狀態】,⽽分散式⼀致性主要關於,⾯對延遲和故障時,如何【協調副本間的狀態】。
  • 一致性模型

    • 線性一致性

      • 最強的一致性模型之一。
        也稱為原⼦⼀致性(atomic consistency) 【7】,強⼀致性(strong consistency),⽴即⼀致性(immediate consistency)或外部⼀致性(external consistency )【8】)。
        它是⼀個新鮮度的保證(recency guarantee)。

      • 線性⼀致性背後的基本思想很簡單:使系統看起來好像只有⼀個資料副本。如果資料庫可以提供只有⼀個副本的假象(即,只有⼀個資料副本)。那麼每個客戶端都會有相同的資料檢視,且不必擔⼼【複製】滯後了。

      • 線性⼀致性的要求是,操作標記的連線總是按時間(從左到右)向前移動,⽽不是向後移動。
        要求新鮮性保證:⼀旦新的值被寫⼊或讀取,所有後續的讀都會看到寫⼊的值,直到它被再次覆蓋。

      • 應用場景

        • 唯⼀性約束
        • 鎖定和領導選舉
        • 跨通道的時序依賴
      • 實現:

        • 方案一:真的只⽤⼀個資料副本。

          • 優點:簡單
          • 缺點:節點失效時服務不可用、甚至有資料丟失風險。
        • 方案二:單主同步複製。

          • 它們可能(protential)是線性⼀致性的 4 。
          • 優點:可靠,單節點失效時,保持服務可用(只讀)。
          • 缺點:效能低下。
        • 方案三:共識演算法。

          • 共識協議包含防⽌【腦裂】和【陳舊副本】的措施。可以安全地實現線性⼀致性儲存。如zookeeper
      • 線性⼀致性的代價

        • 面臨的問題:

          • 如果應⽤需要線性⼀致性:當某些副本因為⽹絡問題與其他副本斷開連線,那麼這些副本掉線時不能處理請求。請求必須等到⽹絡問題解決,或直接返回錯誤。⽆論哪種⽅式,服務都不可⽤(unavailable)。
          • 如果應⽤不需要線性⼀致性:那麼某個副本即使與其他副本斷開連線,也可以獨⽴處理請求(例如多主複製)。在這種情況下,應⽤可以在⽹絡問題前保持可⽤,但其⾏為不是線性⼀致的。
        • CAP定理:不需要線性⼀致性的應⽤對⽹絡問題有更強的容錯能⼒。

          • CAP有時以這種⾯⽬出現:⼀致性,可⽤性和分割槽容忍:三者只能擇其⼆。這種說法很有誤導性【32】,因為⽹絡分割槽並不是⼀個選項:不管你喜不喜歡它都會發⽣【38】。
          • 在⽹絡正常⼯作的時候,系統可以提供⼀致性(線性⼀致性)和整體可⽤性。發⽣⽹絡故障時,你必須在【線性⼀致性】和【整體可⽤性】之間做出選擇。
          • ⼀個更好的表達CAP的⽅法可以是⼀致
            的,或者在分割槽時可⽤【39】。
        • 為了線性一致性犧牲效能和可用性 vs 或者為了提高效能、可用性而使用弱一致性。

  • 順序保證

    • 全序

    • 偏序

    • 順序和因果順序

      • 因果順序不是全序的
      • 關係:線性⼀致性隱含著(implies)因果關係:任何線性⼀致的系統都能正確保持因果性【7】。
      • 線性⼀致性簡單、易懂,但可能會損害系統的效能和可⽤性,尤其是在系統具有嚴重的⽹絡延遲的情況下。
        在許多情況下,看上去需要線性⼀致性的系統,實際上需要的只是因果⼀致性,而因果⼀致性可以更⾼效地實現。
    • 序列號順序

      • 我們可以使⽤序列號(sequence nunber)或時間戳(timestamp)來排序事件。時間戳不⼀定來⾃時鐘,它可以來⾃⼀個【邏輯時鐘】(logical clock),這是【⼀個⽤來⽣成標識操作的數字序列的演算法】,典型實現是使⽤⼀個每次操作⾃增的計數器。

      • 這樣的序列號或時間戳是【緊湊的】(只有⼏個位元組⼤⼩),它提供了⼀個【全序關係】:也就是說每操作都有⼀個唯⼀的序列號,⽽且總是可以⽐較兩個序列號,確定哪⼀個更⼤(即哪些操作後發⽣)。

      • 非因果序列號生成器

        • 適用於主庫不存在情況,可能因為使⽤了多主資料庫或⽆主資料庫,或者因為使⽤了分割槽的資料庫。

        • 實現方案:

          • a。每個節點都可以⽣成⾃⼰獨⽴的⼀組序列號。例如有兩個節點,⼀個節點只能⽣成奇數,⽽另⼀個節點只能⽣成偶數。
          • b。將具有⾜夠⾼解析度的時鐘時間戳附加到每個操作上。
          • c。可以預先分配序列號區塊。例如,節點 A 可能要求從序列號1到1,000區塊的所有權,⽽節點 B 可能要求序列號1,001到2,000區塊的所有權。
        • 問題:⽣成的序列號與因果不⼀致。
          但⽐單⼀主庫的⾃增計數器效能表現要好,並且更具可擴充套件性。

      • 蘭伯特時間戳

        • 定義:蘭伯特時間戳就是兩者的簡單組合:(計數器,節點ID)。它提供了⼀個【全序】:如果你有兩個時間戳,則計數器值⼤者是更⼤的時間戳。如果計數器值相同,則節點ID越⼤的,時間戳越⼤。
        • 特點:Lamport時間戳【解決了非因果序列號生成器的問題】,它提供了與因果關係⼀致的總排序。
        • 關鍵思想:每個節點和每個客戶端跟蹤迄今為⽌所⻅到的最⼤計數器值,並在每個請求中包含這個最⼤計數器值。當⼀個節點收到最⼤計數器值⼤於⾃身計數器值的請求或響應時,它⽴即將⾃⼰的計數器設定為這個最⼤值。
        • 缺點:適用於【事後確定勝利者】場景,需要實時確定的場景會有問題。
    • 全序⼴播(total order broadcast)

      • 但是在分散式系統中,讓所有節點對同⼀個全域性操作順序達成⼀致可能相當棘⼿。在上⼀節中,我們討論了按時間戳或序列號進⾏排序,但發現它還不如單主複製給⼒(如果你使⽤時間戳排序來實現唯⼀性約束,⽽且不能容忍任何錯誤)。

      • 定義:如前所述,單主複製通過選擇⼀個節點作為主庫來確定操作的全序,並在主庫的單個CPU核上對所有操作進⾏排序。
        接下來的挑戰是,如果吞吐量超出單個主庫的處理能⼒,這種情況下【如何擴充套件系統】;以及,如果主庫失效,【如何處理故障切換】。這個問題被稱為全序⼴播。

      • 屬性:全序⼴播通常被描述為在【節點間交換訊息的協議】。它要滿⾜兩個安全屬性,即使節點或⽹絡出現故障:

        • 1.可靠交付(reliable delivery)
          沒有訊息丟失,即如果訊息被傳遞到⼀個節點,它將被傳遞到所有節點。
        • 2.全序交付(totally ordered delivery)*
          訊息以相同的順序傳遞給每個節點。
      • 應用:

        • 1.資料庫狀態機複製(state machine replication):如果每個訊息都代表⼀次資料庫的寫⼊,且每個副本都按相同的順序處理相同的寫⼊,那麼副本間將相互保持⼀致。
        • 2.實現可序列化的事務。
        • 3.實現提供【防護令牌】的鎖服務:序列號可以當成防護令牌⽤,因為它是單調遞增的。在ZooKeeper中,這個序列號被稱為 zxid。
      • 全序廣播 vs 線性一致性儲存

        • 全序廣播是非同步的,訊息保證以固定的順序可靠地傳送,但是【不能保證訊息何時被送達】。
          線性一致性是【新鮮度】的保證,即讀取一定能看見最新的寫入值。

        • 使⽤全序⼴播實現線性⼀致的儲存:

          • 可以通過將全序⼴播當成僅追加⽇志致的CAS操作來實現,選擇衝突寫⼊中的第⼀個作為勝利者,並中⽌後來者,以此確定所有節點對某個寫⼊是提交還是中⽌達成⼀致。
        • 使⽤線性⼀致性儲存實現全序⼴播:

          • 最簡單的⽅法是假設你有⼀個線性⼀致的暫存器來儲存⼀個整數,並且有⼀個原⼦⾃增並返回操作【28】。
          • 該演算法很簡單:每個要通過全序⼴播傳送的訊息⾸先對線性⼀致暫存器執⾏⾃增並返回操作。然後將從暫存器獲得的值作為序列號附加到訊息中。然後你可以將訊息傳送到所有節點(重新傳送任何丟失的訊息),⽽收件⼈將按序列號連續傳送訊息。
  • 分散式事務與共識

    • 共識是分散式計算中最重要也是最基本的問題之⼀。

      • 現在我們已經討論了複製(第5章),事務(第7章),系統模型(第8章),線性⼀致以及全序(本章),我們終於準備好解決共識問題了。

      • 場景

        • 領導選舉
        • 原子提交:在⽀持跨多節點或跨多分割槽事務的資料庫中,我們必須讓所有節點對事務的結果達
          成⼀致:要麼全部中⽌/回滾(如果出現任何錯誤),要麼它們全部提交(如果沒有出錯)。這個共識的例⼦被稱為原⼦提交(atomic commit)問題
    • 原子提交與二階段提交2PC

      • 單節點原子提交

        • 對於在單個資料庫節點執⾏的事務,原⼦性通常由儲存引擎實現。當客戶端請求資料庫節點提交事務時,資料庫將使事務的寫⼊持久化(通常在預寫式⽇志中:參閱“使B樹可靠”),然後將【提交記錄】追加到磁碟中的⽇志⾥。如果資料庫在這個過程中間崩潰,當節點重啟時,事務會從⽇志中恢復:如果提交記錄在崩潰之前成功地寫⼊磁碟,則認為事務被提交;否則來⾃該事務的任何寫⼊都被回滾。
        • 在單個節點上,事務的提交主要取決於資料持久化落盤的順序:⾸先是資料,然後是【提交記錄】。事務提交或終⽌的關鍵決定時刻是磁碟完成寫⼊【提交記錄】的時刻:在此之前,仍有可能中⽌(由於崩潰),但在此之後,事務已經提交,即使資料庫崩潰。因此,是單⼀的裝置(連線到單個磁碟驅動的控制器,且掛載在單臺機器上)使得提交具有原⼦性。
      • 分散式原子提交

        • 引言:如果⼀個事務中涉及多個節點,僅向所有節點傳送提交請求並獨⽴提交每個節點的事務是不夠的。這樣很容易發⽣【違反原⼦性】的情況:提交在某些節點上成功,⽽在其他節點上失敗。
          兩階段提交(two-phase commit) 是⼀種⽤於實現跨多個節點的原⼦事務提交的演算法,即確保所有節點提交或所有節點中⽌。 它是分散式資料庫中的經典演算法。

          • PS:事務提交必須是不可撤銷的 —— 事務提交之後,你不能改變主意,並追溯性地中⽌事務。這個規則的原因是,⼀旦資料被提交,其結果就對其他事務可⻅,因此其他客戶端可能會開始依賴這些資料。這個原則構成了【讀已提交隔離等級】的基礎,在“讀已提交”⼀節中討論了這個問題。如果⼀個事務在提交後被允許中⽌,所有那些讀取了已提交卻⼜被追溯宣告不存在資料的事務也必須回滾。
        • 兩階段提交

          • 兩階段提交(2PC, twophase commit)演算法是解決原⼦提交問題最常⻅的辦法。2PC是⼀種共識演算法。

          • 名詞

            • 協調者coordinator/事務管理器transaction manager

              • a。協調者通常不會出現在單節點事務中。
                b。協調者通常在請求事務的相同應⽤程式中以庫的形式實現(例如,嵌⼊在Java EE容器中),但也可以是單ᇿ的程式或服務。
            • 參與者(participate)

              • 正常情況下,2PC事務以應⽤在多個資料庫節點上讀寫資料開始。我們稱這些資料庫節點為參與者。
          • 基本流程

            • 階段 1 : 當應⽤準備提交時,協調者傳送⼀個【準備(prepare)請求】到每個節點,詢問它們是否能夠提交,並跟蹤參與者的響。
            • 階段 2 :
              a。如果所有參與者都回答“是”,表示它們已經準備好提交,那麼協調者發出【提交(commit)請求】,然後提交真正發⽣。
              b。如果任意⼀個參與者回覆了“否”,則協調者在階段2 中向所有節點傳送【中⽌(abort)請求】。
          • 類比:⻄⽅的傳統婚姻儀式

            • 司儀-> 協調者, 新郎新娘 -> 參與者
        • 系統承諾:

          • 承諾1.當參與者投票“是”時,它承諾它稍後肯定能夠提交(儘管協調者可能仍然選擇放棄)。

            • 參與者收到準備請求時,需要確保在任意情況下都的確可以提交事務。這包括將所有事務資料寫⼊磁碟(出現故障,電源故障,或硬碟空間不⾜都不能是稍後拒絕提交的理由)以及檢查是否存在任何衝突或違反約束。通過向協調者回答“是”,節點承諾,只要請求,這個事務⼀定可以不出差錯地提交。換句話說,參與者放棄了中⽌事務的權利,但沒有實際提交。
          • 承諾2.⼀旦協調者做出決定,這⼀決定是不可撤銷的。

            • 當協調者收到所有準備請求的答覆時,會就提交或中⽌事務作出明確的決定(只有在所有參與者投贊成票的情況下才會提交)。協調者必須把這個決定寫到磁碟上的事務⽇志中,如果它隨後就崩潰,恢復後也能知道⾃⼰所做的決定。這被稱為提交點(commit point)。
            • ⼀旦協調者的決定落盤,提交或放棄請求會傳送給所有參與者。如果這個請求失敗或超時,協調者必須永遠保持重試,直到成功為⽌。沒有回頭路:如果已經做出決定,不管需要多少次重試它都必須被執⾏。如果參與者在此期間崩潰,事務將在其恢復後提交——由於參與者投了贊成,因此恢復後它不能拒絕提交。
          • 兩階段提交協議包含的2個關鍵的“不歸路”點,保證了2PC的原⼦性。

          • 類比:⻄⽅的傳統婚姻儀式

        • 協調者失效

          • 存疑:⼀旦參與者收到了準備請求並投了“是”,就不能再單⽅⾯放棄 —— 必須等待協調者回答事務是否已經提交或中⽌。如果此時協調者崩潰或⽹絡出現故障,參與者什麼也做不了只能等待。參與者的這種事務狀態稱為【存疑(in doubt)】的 或不確定(uncertain)的。
          • 完成2PC的唯⼀⽅法是等待協調者恢復。這就是為什麼協調者必須在向參與者傳送提交或中⽌請求之前,將其提交或中⽌決定寫⼊磁碟上的事務⽇志:協調者恢復後,通過讀取其事務⽇志來確定所有存疑事務的狀態。
      • 三階段提交

        • 兩階段提交被稱為阻塞(blocking)原⼦提交協議,因為存在2PC可能卡住並等待協調者恢復的情況。
        • 三階段提交(3PC)的演算法:3PC假定⽹絡延遲有界,節點響應時間有限;在⼤多數具有⽆限⽹絡延遲和程式暫停的實際系統中它並不能保證原⼦性。
        • 在具有⽆限延遲的⽹絡中,超時並不是⼀種可靠的故障檢測機制,因為即使沒有節點崩潰,請求也可能由於⽹絡問題⽽超時。出於這個原因,2PC仍然被使⽤,儘管⼤家都清楚可能存在協調者故障的問題。
    • 實踐中的分散式事務

      • 缺點:分散式事務的名聲譭譽參半。一方面它難以實現,另一方面效能損失嚴重,據報告稱Mysql中的分散式事務比單節點事務慢10倍以上。

        • 成本來源:兩階段提交所固有的效能成本,⼤部分是由於崩潰恢復所需的額外強制刷盤( fsync )【88】以及額外的⽹絡往返。
      • XA事務

        • XA(擴充套件架構(eXtended Architecture)的縮寫)是跨異構技術實現兩階段提交的標準。
        • XA不是⼀個⽹絡協議——它只是⼀個⽤來與事務協調者連線的C API。
        • XA假定你的應⽤使⽤⽹絡驅動或客戶端庫來與參與者進⾏通訊(資料庫或訊息服務)。如果驅動⽀持XA,則意味著它會調⽤XA API 以查明操作是否為分散式事務的⼀部分 —— 如果是,則將必要的資訊發往資料庫伺服器。驅動還會向協調者暴露回撥接⼝,協調者可以通過回撥來要求參與者準備,提交或中⽌。
        • 事務協調者需要實現XA API。
      • 存疑持有鎖

        • 為什麼我們這麼關⼼存疑事務?系統的其他部分就不能繼續正常⼯作,⽆視那些終將被清理的存疑事務嗎?
          問題在於鎖(locking)。如果要使⽤可序列化的隔離等級,則使⽤兩階段鎖定的資料庫會為事務所讀取的⾏加上共享鎖(參⻅“兩階段鎖定(2PL)”)。當這些鎖被持有時,其他事務不能修改這些⾏。直到存疑事務被解決。

        • 從協調者故障中恢復

          • 理論上,如果協調者崩潰並重新啟動,它應該⼲淨地從⽇志中恢復其狀態,並解決任何存疑事務。然⽽在實踐中,【孤⽴(orphaned)的存疑】事務確實會出現,即⽆論出於何種理由,協調者⽆法確定事務的結果(例如事務⽇志已經由於軟體錯誤丟失或損壞)。
            這些事務⽆法⾃動解決,所以它們永遠待在資料庫中,持有鎖並阻塞其他事務。即使重啟資料庫伺服器也⽆法解決這個問題。
            唯⼀的出路是讓管理員⼿動決定提交還是回滾事務。
          • 啟發式決策(heuristic decistions):
            許多XA的實現都有⼀個叫做啟發式決策的緊急逃⽣艙⼝:允許參與者單⽅⾯決定放棄或提交⼀個存疑事務,⽽⽆需協調者做出最終決定。要清楚的是,這⾥啟發式是【可能破壞原⼦性】(probably breaking atomicity)的委婉說法,因為它【違背了兩階段提交的系統承諾】。

因此,啟發式決策只是為了逃出災難性的情況⽽準備的,⽽不是為了⽇常使⽤的。

	- 分散式事務的限制

		- 核⼼認識:事務協調者本身就是⼀種資料庫(儲存了事務的結果),需要像其他重要資料庫⼀樣⼩⼼地打交道。
		- 限制1.如果協調者沒有複製,只是在單臺機器上執行,那麼他是整個系統的失效單點。ps:即存疑問題
		- 限制2. 應⽤伺服器不再是⽆狀態的。

許多伺服器端應⽤都是使⽤⽆狀態模式開發的(受HTTP的⻘睞),所有持久狀態都儲存在資料庫中,因此具有應⽤伺服器可隨意按需新增刪除的優點。
但是,當協調者成為應⽤伺服器的⼀部分時,它會改變部署的性質。突然間,協調者的⽇志成為持久系統狀態的關鍵部分—— 與資料庫本身⼀樣重要,因為協調者⽇志是為了在崩潰後恢復存疑事務所必需的。這樣的應⽤伺服器不再是⽆狀態的了。
- 限制3. 由於XA需要相容各種資料系統,因此它必須是所有系統的最⼩公分⺟。例如,它不能檢測不同系統間的死鎖,因為這將需要⼀個標準協議來讓系統交換每個事務正在等待的鎖的資訊。以及無法與SSI 協同⼯作,因為這需要⼀個跨系統定衝突的協議。
- 限制4. 2PC成功提交⼀個事務需要所有參與者的響應。因此,如果系統的任何部分損壞,事務也會失敗。因此,分散式事務⼜有【擴⼤失效】(amplifying failures)的趨勢,這⼜與我
們構建容錯系統的⽬標背道⽽馳。

- 容錯共識

	- 概念

		- ⾮正式地,共識意味著讓⼏個節點就某事達成⼀致。共識問題通常形式化如下:⼀個或多個節點可以提議(propose)某些值,⽽共識演算法決定(decides)採⽤其中的某個值。

	- 共識演算法必須滿足的性質

		- a。協商一致性(Uniform agreement):所有節點都接受相同的決議。
		- b。誠實性(Integrity):所有節點不能反悔,即對一項提議不能有兩次決定。
		- c。合法性(Validity):如果一個節點決定了值v,則v一定是由某個節點所提議的。
		- d。可終止性(Termination):節點如果沒有崩潰,則最終一定可以達成協議。

	- 可終⽌性是⼀種活性屬性,⽽另外三種是安全屬性。

		- i。協商一致性和誠實性定義了共識的【核⼼思想】:決議一直的結果,⼀旦決定,你就不能改變。

ii。合法性屬性束腰式為了排除一些無意義的方案。
iii。可終止性引入了【容錯】思想:⼀個共識演算法不能簡單地永遠閒坐著等死 ,它必須取得進展。即使部分節點出現故障,其他節點也必須達成⼀項決定。

	- 共識演算法和全序⼴播

		- ⼤多數這些演算法實際上並不直接使⽤這⾥描述的形式化模型(提議並決定單個值,同時滿足以上4個屬性)。取而代之的是全序⼴播演算法,全序⼴播將訊息按照相同的順序傳送到所有節點,有且只有一次。
		- 全序⼴播相當於重複進⾏多輪共識(每一輪共識的決定對應於一條訊息):

			- 由於協商一致性,所有節點決定以相同的順序傳送相同的訊息。
			- 由於誠實性,訊息不能重複。
			- 由於合法性,訊息不會被破壞,也不會憑空捏造。
			- 由於可終止性,訊息不會丟失。

	- 時代編號和法定⼈數

		- 迄今為⽌所討論的所有共識協議,在內部都以某種形式使⽤⼀個領導者,但它們並不能保證領導者是獨⼀⽆⼆的。相反,它們可以做出更弱的保證:協議定義了⼀個時代編號(epoch number),並確保在每個時代中,領導者都是唯⼀的。

			- 時代編號在Paxos中稱為投票編號(ballot number,在檢視戳複製中成為檢視編號(view number,以及在Raft中稱為任期號碼(term number)),

		- 對領導者想要做出的每⼀個決定,都必須將提議值傳送給其他節點,並等待法定⼈數的節點響應並贊成提案。法定⼈數通常(但不總是)由多數節點組成【105】。只有在沒有意識到任何帶有更⾼時代編號的領導者的情況下,⼀個節點才會投票贊成提議。
		- 因此,我們有兩輪投票:第⼀次是為了選出⼀位領導者,第⼆次是對領導者的提議進⾏表決。關鍵的洞察在於,這兩次投票的法定⼈群必須相互【重疊】(overlap):如果⼀個提案的表決通過,則⾄少得有⼀個參與投票的節點也必須參加過最近的領導者選舉【105】

	- 共識的侷限性

		- 優點

			- 共識演算法對於分散式系統來說是⼀個巨⼤的突破:它為其他充滿不確定性的系統帶來了基礎的安全屬性(協商一致性,誠實性和合法性),然⽽它們還能保持容錯(只要多數節點正常⼯作且可達,就能取得進展)。

		- 缺點

			- 在非同步複製模式下,節點發生故障切換時,一些已經提交的資料可能會丟失。
			- 共識系統通常依靠超時來檢測失效的節點。在網路糟糕的情況下,會導致頻繁的領導者選舉,繼而導致糟糕的效能表現,
			- 共識系統需要至少三個節點才能容忍單節點故障,如果⽹絡故障切斷了某些節點同其他節點的連線,則只有多數節點所在的⽹絡可以繼續⼯作,其餘部分將被阻塞(參閱“線性⼀致性的代價”)。

- 成員與協調服務
  • 小結

    • 在本章中,我們從⼏個不同的⻆度審視了關於⼀致性與共識的話題。我們深⼊研究了:

      • 線性一致性:最強的一致性模型,其⽬標是使多副本資料看起來好像只有⼀個副本⼀樣,並使其上所有操作都原⼦性地⽣效。線性⼀致性簡單易懂,但效能低下,尤其是在網路延遲很大的環境中。
      • 因果一致性:因果⼀致性為我們提供了⼀個較弱的⼀致性模型:某些事件可以是併發的,所以版本歷史就像是⼀條不斷分叉與合併的時間線。因果⼀致性沒有線性⼀致性的協調開銷,⽽且對⽹絡問題的敏感性要低得多。
      • 即使做到了因果順序,但有些事情也需要通過達成共識才能做出決定。達成共識意味著所有節點⼀致同意所做決定,且這⼀決定不可撤銷。
    • 通過深⼊挖掘,我們發現很⼴泛的⼀系列問題實際上都可以歸結為共識問題,並且彼此等價。
      從這個意義上來講,如果你有其中之⼀的解決⽅案,就可以輕易將它轉換為其他問題的解決⽅案。這些等價問題包括:

      • 線性⼀致性的CAS暫存器
      • 原⼦事務提交
      • 全序⼴播
      • 鎖和租約
      • 成員/協調服務
      • 唯⼀性約束
    • 單領導者資料庫可以提供線性⼀致性,唯一性約束,完全有序的複製日誌等,但也需要共識演算法

      • 針對領導者節點失效或者網路中斷導致領導者不可達的異常問題,應對方案有三種:

        • i。等待領導者節點恢復,接受系統將在這段時間阻塞的事實。但不能達成共識,因為不滿足可終止性。
          ii。人工故障切換,故障切換的速度受人類行動速度的限制。
          iii。使用共識演算法自動選擇一個新的領導者。
    • 如果你發現⾃⼰想要解決的問題可以歸結為共識,並且希望它能容錯,使⽤⼀個類似ZooKeeper的東⻄是明智之舉。像ZooKeeper這樣的⼯具為應⽤提供了“外包”的共識、故障檢測和成員服務。

相關文章