分散式資料庫的複製原理 - Quastor

banq發表於2022-01-20

如果您對後端工程感興趣,那麼設計資料密集型應用程式 (DDIA) 是必讀的。資料工程世界充滿了流行語和炒作,但Martin Kleppman在分解所有核心技術方面做得非常出色。

這是 DDIA 關於複製的第 5 章的摘要。

複製是將資料副本儲存在多臺不同機器上的地方。這些機器通過網路連線,因此您的後端伺服器都可以訪問它們。

您現在使用的是由多臺機器組成的分散式資料庫,而不是將單臺機器用作資料庫。

您希望在多臺計算機上覆制資料的原因有多種

  1. 減少延遲——印度的使用者可以向位於德里的節點傳送請求,而美國的使用者可以向位於紐約的節點傳送請求。
  2. 提高可用性- 如果其中一個節點由於某種原因出現故障,您將擁有另一個可以接管並響應資料請求的節點。
  3. 增加讀取吞吐量- 多個節點能夠響應讀取查詢,而不是僅讓 1 臺機器完成讀取請求的所有工作。許多工作負載都是讀擴充套件的(主要是讀和一小部分寫),所以提高讀吞吐量是非常有幫助的。

複製的難點在於處理複製資料的更改。

當您收到修改資料庫的寫入請求時,如何確保所有副本都反映此寫入請求?

如何阻止未更新的副本響應陳舊資料來讀取請求?

有 3 種流行的策略可以將更改寫入所有副本

  1. 單主複製——一個副本節點被指定為領導者。其他節點是追隨者。寫入請求傳送到領導節點,領導節點隨後會將更改傳播給追隨者。 這是 PostgreSQL、MongoDB、MySQL 等許多資料庫使用的複製策略。
  2. 多主複製 - 這類似於單主Single Leader複製,但現在多個節點可以充當領導者並處理寫入請求。多主複製通常使用外部工具實現,例如用於 MySQL 的 Tungstein Replicator、用於 PostgreSQL 的 BDR 和用於 Oracle 的 GoldenGate。
  3. 無主複製 -所有副本節點都可以接受來自客戶端的寫入請求,因此沒有領導節點。 Riak 和 Cassandra 是使用無領導複製策略的資料庫示例。亞馬遜在其內部 Dynamo 系統中使用了無領導複製,因此 Riak 和 Cassandra 也被稱為 Dynamo 風格。

注意 - Amazon 的 Dynamo 系統與 Amazon 的 DynamoDB 不同。DynamoDB 基於 Dynamo 的許多原則,但有不同的實現。DynamoDB 使用單主者複製。 

幾乎所有分散式資料庫都使用這三種方法中的一種,它們各有利弊。

然而,單主複製是分散式資料庫最流行的複製策略。因此,我們將進一步深入研究單主複製。

單主複製工作原理如下

  1. 其中一個副本被設計為領導者。來自客戶端的寫請求將被髮送給領導者,領導者會將新資料寫入其本地儲存。
  2. 其他副本稱為追隨者。每當領導者將新資料寫入其本地儲存時,它也會將資料更改傳送給所有追隨者。
  3. 每個跟隨者從領導者那裡獲取資料更改日誌,並通過應用所有新寫入來更新其本地資料庫副本。
  4. 當客戶端想要從資料庫中讀取資料時,可以將讀取請求查詢到資料庫中的任何節點——領導者或追隨者。

對資料庫的寫入可以是非同步的、同步的和半同步的。

對於非同步寫入,領導者將獲取客戶端的寫入請求並更新其自己的本地儲存。然後,它會響應說寫入成功。響應後,領導者將向所有跟隨者節點傳送一條訊息,其中包含來自客戶端寫入請求的資料更改。

通過同步寫入,領導者將首先確保每個跟隨者節點都已將資料更改寫入其本地資料庫。一旦主節點收到所有跟從節點的確認,它會回覆一條寫成功的訊息。

對於半同步寫入,主節點會等待特定數量的從節點的寫入確認(這個引數可以配置),直到它響應寫入成功的訊息。

實際上,很少使用同步寫入。使用同步寫入策略,寫入請求將花費極長的時間(因為您必須等待每個從節點跟隨者響應)並且會經常失敗(任何時候一個或多個跟隨者節點沒有響應)。

因此,工程師通常使用半同步策略或非同步策略。

半同步和非同步寫入策略之間的權衡歸結為您希望處理寫入請求的速度(非同步寫入更快)以及您希望寫入請求的永續性(非同步寫入策略在以下情況下丟失寫入資料的可能性更大)領導節點在向跟隨者傳送寫入更改之前崩潰)。

單主複製經常出現的兩個問題是

  • 處理節點中斷
  • 複製滯後

  

處理節點中斷

節點中斷是不可避免的,尤其是當您使用具有許多跟隨節點的大型分散式資料庫時。

有兩種型別的節點中斷:追隨從節點中斷和主節點領導者中斷。

  • 追隨者從節點失敗:Catch-up recovery

如果跟隨節點發生故障,那麼它可以很容易地恢復。追隨者在本地非易失性儲存中記錄從領導者接收到的所有資料更改。因此,追隨者知道它處理的最後一筆交易。

追隨者將向領導者查詢自上次事務以來發生的所有更改,然後更新其本地狀態以匹配當前狀態。

  • 主節點領導者失敗:故障轉移

處理領導者的失敗更加棘手。需要將追隨者節點之一提升為新的領導者,並且必須重新配置客戶端以將其寫入傳送給新的領導者。其他追隨者也必須開始使用來自新領導者的資料更改。

此過程稱為故障轉移。

故障轉移過程有很多可能出錯的地方

  • 如果使用非同步複製,那麼新的領導者在失敗之前可能還沒有收到舊領導者的所有寫入。這意味著較弱的耐用性保證。
  • 當原來的領導者重新上線時,他們可能被錯誤地配置為認為他們仍然是領導者。這是一種常見的故障,通常被稱為裂腦。
  • 由於您的資料庫在故障轉移過程發生時無法接受新的寫入,因此可能會出現負載問題。如果領導節點經常失敗,那麼這可能會阻塞資料庫。

 

複製滯後

當您使用具有半同步或非同步寫入的單領導主節點複製策略時,您會經常遇到一致性問題,即客戶端將從尚未完全更新的跟隨從節點讀取陳舊資料。

這種不一致是一種暫時的狀態,如果你稍等片刻,那麼所有的追隨者最終都會趕上來。因此,這種效應稱為最終一致性。

但是,最終一致性是一個模糊的術語,並沒有指定複製滯後多長時間。可能是幾秒鐘甚至幾分鐘。

因此,即使具有“最終一致性”,複製滯後對您的使用者來說也是一個大問題。

為了緩解這些問題,您可以使用多種方法來減少使用者面臨的一些常見問題。

我們將介紹其中一些方法以及它們解決的問題。

  • 讀你自己的寫入Read Your Own Writes

假設您正在構建一個 Twitter :使用者可以從他們的計算機上釋出一條推文,這將向您的分散式資料庫傳送一個寫請求。

這個寫請求是非同步複製的,所以主節點在改變本地狀態後會響應寫成功。然後,它將更改傳送到所有跟隨節點。

如果使用者在釋出推文後立即重新整理頁面並嘗試重新載入,則對使用者先前推文的新讀取請求可能會轉到尚未通知新推文的關注者節點。因此,使用者的推特資料在他重新整理後不會顯示他的新推文。顯然,這可能會讓使用者非常沮喪。

讀你自己的寫入一致性是一種解決方案,它保證如果使用者重新載入頁面,他們將始終看到他們自己提交的任何更新。它不對其他使用者做出任何承諾。

可以通過多種方式實現。一種可能的方法是跟蹤使用者上次提交更新的時間。如果他們在過去一分鐘內提交了更新,那麼他們的讀取請求應該由領導主節點處理。

  • 單調讀取

非同步複製會導致一些追隨者節點在更新方面落後於其他追隨者節點。因此,使用者可能會訪問該網站並從最新的關注者節點獲取推文。之後,他可能會重新載入他的頁面,然後從落後的追隨者節點獲取推文。這將導致他的 twitter 提要“回到過去”,因為他獲得的資料是陳舊的。這顯然意味著糟糕的使用者體驗。

單調讀取是這種異常不會發生的保證:實現此保證的一種方法是確保每個使用者始終從同一個跟隨者節點讀取(不同的使用者可以從不同的副本讀取)。可以根據使用者 ID 的雜湊值而不是隨機地選擇跟隨從節點。

  •  一致性字首讀取

假設您的 twitter 應用上有使用者 A 和使用者 B。使用者 A 在推特上釋出了一張他的狗的照片。使用者 B 在推特上回復了那張照片,並稱讚了這隻狗。

兩條推文之間存在因果關係,如果您看不到使用者 A 的推文,使用者 B 的回覆推文就沒有任何意義。

現在使用者 C 關注使用者 A 和使用者 B。如果使用者 A 的推文比使用者 B 的推文經歷了更多的複製滯後,那麼使用者 C 可能會看到使用者 B 的回覆推文而沒有收到使用者 A 的推文。

一致性字首讀取是解決此異常的保證:該保證表明,如果一系列寫入以特定順序發生,那麼任何閱讀這些寫入的人都會看到它們以相同的順序出現。

如果資料庫始終以相同的順序應用寫入,則可以解決此問題,但是當您的資料庫被分片時會出現複雜情況。檢視 DDIA 瞭解更多詳情。

 

相關文章