01.27 「數據庫架構」Apache Couchdb 最終一致性

1.3 最終一致性

在上一個文檔“為什麼選擇CouchDB?”中,我們看到CouchDB的靈活性使我們能夠隨著應用程序的增長和變化而發展數據。在本主題中,我們將探討CouchDB的“細化”工作如何提高應用程序的簡單性,並幫助我們自然地構建可擴展的分佈式系統。

1.3.1 與Grain合作

分佈式系統是可以在廣泛的網絡上穩定運行的系統。網絡計算的一個特殊功能是網絡鏈接可能會消失,並且有許多策略可以管理這種類型的網絡分段。 CouchDB與其他數據庫的不同之處在於,它接受最終的一致性,而不是像RDBMS或Paxos這樣在原始可用性之前放置絕對一致性。這些系統的共同點是認識到,當許多人同時訪問數據時,數據的行為會有所不同。在優先考慮一致性,可用性或分區容忍的哪些方面時,他們的方法有所不同。

工程分佈式系統是棘手的。隨著時間的推移,您將要面對的許多警告和“陷阱”並不是立即顯而易見的。我們還沒有所有解決方案,而且CouchDB並非萬能藥,但是當您使用CouchDB的精髓而不是反對時,阻力最小的途徑將使您自然地擴展應用程序。

當然,構建分佈式系統僅僅是開始。一個僅擁有一半時間可訪問數據庫的網站幾乎一文不值。不幸的是,傳統的關係數據庫一致性方法使應用程序程序員很容易依賴全局狀態,全局時鐘和其他高可用性,甚至沒有意識到自己正在這樣做。在研究CouchDB如何提高可伸縮性之前,我們將研究分佈式系統面臨的約束。當我們看到了當您的應用程序的各個部分無法相互依賴時會出現的問題之後,我們將看到CouchDB提供了一種直觀且有用的方式來圍繞高可用性對應用程序進行建模。

1.3.2 CAP定理

CAP定理描述了用於在網絡之間分佈應用程序邏輯的幾種不同策略。 CouchDB的解決方案使用複製在參與的節點之間傳播應用程序更改。這是與共識算法和關係數據庫根本不同的方法,共識算法和關係數據庫在一致性,可用性和分區容忍度的不同交集處運行。

CAP定理,如圖1所示。CAP定理確定了三個不同的問題:

  • 一致性:即使併發更新,所有數據庫客戶端也可以看到相同的數據。
  • 可用性:所有數據庫客戶端都可以訪問某些版本的數據。
  • 分區容限:數據庫可以拆分到多個服務器上。

選擇兩個。

「數據庫架構」Apache Couchdb 最終一致性

當系統增長到足以使單個數據庫節點無法處理施加在其上的負載時,明智的解決方案是添加更多服務器。添加節點時,我們必須開始考慮如何在它們之間分區數據。我們有幾個共享完全相同數據的數據庫嗎?我們是否將不同的數據集放在不同的數據庫服務器上?我們是否只允許某些數據庫服務器寫入數據,而讓其他服務器處理讀取?

無論採用哪種方法,我們都會遇到的一個問題是使所有這些數據庫服務器保持同步。如果您將某些信息寫入一個節點,那麼如何確保對另一臺數據庫服務器的讀取請求反映了此最新信息?這些事件可能相隔毫秒。即使只有少量的數據庫服務器,此問題也會變得非常複雜。

當絕對至關重要的是,所有客戶端都必須看到一致的數據庫視圖時,一個節點的用戶將必須等待其他任何節點達成協議,才能讀取或寫入數據庫。在這種情況下,我們看到可用性在一致性方面倒退了。但是,在某些情況下,可用性比一致性要好:

系統中的每個節點都應該能夠純粹基於本地狀態做出決策。如果您需要在高負載下做某事且發生故障並且需要達成協議,那麼您會迷失方向。如果您擔心可擴展性,那麼任何迫使您達成協議的算法最終都會成為瓶頸。以此為前提。

—亞馬遜首席技術官兼副總裁沃納·沃格斯(Werner Vogels)

如果優先考慮可用性,我們可以讓客戶端將數據寫入數據庫的一個節點,而無需等待其他節點達成協議。如果數據庫知道如何照顧節點之間的這些操作,那麼我們將獲得某種“最終一致性”,以換取高可用性。對於許多應用來說,這是一個令人驚訝的適用折衷。

與傳統的關係數據庫不同,傳統的關係數據庫必須對每個執行的操作進行數據庫範圍的一致性檢查,而CouchDB使得構建應用程序變得非常簡單,而這些應用程序卻犧牲了即時一致性,以簡化簡單分發帶來的巨大性能提升。

1.3.3 本地一致性

在嘗試瞭解CouchDB如何在群集中運行之前,重要的是我們瞭解單個CouchDB節點的內部工作原理。 CouchDB API旨在提供圍繞數據庫核心的便捷但精簡的包裝。通過仔細研究數據庫核心的結構,我們將更好地瞭解圍繞它的API。

1.3.3.1 數據的Key

CouchDB的核心是功能強大的B樹存儲引擎。 B樹是一種排序的數據結構,允許以對數時間進行搜索,插入和刪除。如圖2所示。對視圖請求的剖析表明,CouchDB使用此B樹存儲引擎存儲所有內部數據,文檔和視圖。如果我們理解一個,我們將全部理解。

「數據庫架構」Apache Couchdb 最終一致性

CouchDB使用MapReduce來計算視圖的結果。 MapReduce利用了兩個函數,即“ map”和“ reduce”,它們分別應用於每個文檔。能夠隔離這些操作意味著視圖計算可以進行並行和增量計算。更重要的是,由於這些函數產生鍵/值對,因此CouchDB能夠將它們按鍵排序插入B樹存儲引擎。通過鍵或鍵範圍進行的查找是使用B樹的極其有效的操作,用大O表示法分別表示為O(log N)和O(log N + K)。

在CouchDB中,我們按鍵或鍵範圍訪問文檔並查看結果。這是對CouchDB的B樹存儲引擎上執行的基礎操作的直接映射。與文檔插入和更新一起,這種直接映射是我們將CouchDB的API描述為圍繞數據庫核心的薄包裝的原因。

只能通過鍵訪問結果是一個非常重要的限制,因為它使我們獲得了巨大的性能提升。除了大幅提高速度外,我們還可以在多個節點上劃分數據,而不會影響我們獨立查詢每個節點的能力。正是由於這些原因,BigTable,Hadoop,SimpleDB和memcached通過鍵限制了對象查找。

1.3.3.2 無鎖

關係數據庫中的表是單個數據結構。如果要修改表(例如,更新行),數據庫系統必須確保沒有其他人試圖更新該行,並且在更新該行時沒有人可以從該行中讀取數據。解決此問題的常用方法是使用鎖。如果多個客戶端要訪問一個表,則第一個客戶端將獲得鎖,從而使其他所有人都在等待。當第一個客戶的請求得到處理時,下一個客戶將獲得訪問權限,而其他人都將等待,依此類推。即使是並行到達請求,這種串行執行請求也會浪費大量服務器的處理能力。在高負載下,關係數據庫比進行任何實際工作要花費更多的時間來確定允許誰執行什麼工作以及按照什麼順序執行。

注意

現代的關係數據庫通過在幕後實施MVCC來避免鎖定,但對最終用戶隱藏了MVCC,要求它們協調單個行或字段的併發更改。

CouchDB使用多版本併發控制(MVCC)代替鎖,來管理對數據庫的併發訪問。圖3. MVCC表示沒有鎖定說明了MVCC和傳統鎖定機制之間的差異。 MVCC意味著CouchDB即使在高負載下也可以一直全速運行。請求是並行運行的,從而充分利用了服務器必須提供的每最後一滴處理能力。

「數據庫架構」Apache Couchdb 最終一致性

圖3. MVCC意味著沒有鎖定

CouchDB中的文檔已經過版本控制,就像在常規版本控制系統(例如Subversion)中一樣。如果要更改文檔中的值,請創建該文檔的全新版本並將其保存在舊版本上。完成此操作後,您將獲得同一文檔的兩個版本,一箇舊版本,一個新版本。

這如何提供對鎖的改進?考慮一組想要訪問文檔的請求。第一個請求讀取文檔。在處理過程中,第二個請求更改了文檔。由於第二個請求包含文檔的全新版本,因此CouchDB可以簡單地將其附加到數據庫,而不必等待讀取請求完成。

當第三個請求要讀取相同的文檔時,CouchDB將其指向剛剛編寫的新版本。在整個過程中,第一個請求可能仍在讀取原始版本。

讀取請求在請求開始時始終會看到您數據庫的最新快照。

1.3.4 驗證方式

作為應用程序開發人員,我們必須考慮應該接受什麼樣的輸入以及應該拒絕什麼輸入。在傳統的關係數據庫中對複雜數據進行這種類型的驗證的表達能力尚有許多不足之處。幸運的是,CouchDB提供了一種從數據庫內部執行按文檔驗證的強大方法。

CouchDB可以使用類似於MapReduce的JavaScript函數來驗證文檔。每次您嘗試修改文檔時,CouchDB都會通過驗證功能以傳遞現有文檔的副本,新文檔的副本以及其他信息的集合,例如用戶身份驗證詳細信息。驗證功能現在可以批准或拒絕更新。

通過使用Grain並讓CouchDB為我們做到這一點,我們為自己節省了大量的CPU週期,否則這些CPU週期將被用於從SQL序列化對象圖,將它們轉換為域對象並使用這些對象進行應用程序級驗證。

1.3.5 分佈式一致性

對於大多數數據庫而言,在單個數據庫節點內維護一致性相對容易。當您嘗試維護多個數據庫服務器之間的一致性時,真正的問題開始浮出水面。如果客戶端在服務器A上執行寫操作,我們如何確保它與服務器B或C或D一致?對於關係數據庫而言,這是一個非常複雜的問題,整本書都專門針對其解決方案。您可以使用多主機,單主機,分區,分片,直寫式高速緩存以及各種其他複雜技術。

1.3.6 增量複製

CouchDB的操作在單個文檔的上下文中進行。由於CouchDB通過使用增量複製實現了多個數據庫之間最終的一致性,因此您不必擔心數據庫服務器能夠保持持續的通信。增量複製是在服務器之間定期複製文檔更改的過程。我們能夠構建所謂的無共享數據庫集群,其中每個節點都是獨立且自給自足的,在整個系統中不存在任何爭用點。

需要擴展您的CouchDB數據庫集群嗎?只需投入另一臺服務器即可。

如圖4所示。在CouchDB節點之間進行增量複製,並使用CouchDB進行增量複製,您可以在任意兩個數據庫之間隨時隨地同步數據。複製後,每個數據庫都可以獨立工作。

您可以使用此功能通過cron之類的作業調度程序在群集內或數據中心之間同步數據庫服務器,也可以使用它在便攜式計算機上同步數據與筆記本電腦以進行離線工作。可以按常規方式使用每個數據庫,並且以後可以在兩個方向上同步數據庫之間的更改。

「數據庫架構」Apache Couchdb 最終一致性

當您在兩個不同的數據庫中更改同一文檔並希望彼此同步時會發生什麼? CouchDB的複製系統帶有自動衝突檢測和解決方案。當CouchDB在兩個數據庫中都檢測到文檔已被更改時,它將標記該文檔為衝突文檔,就像它們在常規版本控制系統中一樣。

這並不像第一次聽起來那樣麻煩。如果在複製過程中兩個版本的文檔發生衝突,則勝出版本將另存為文檔歷史記錄中的最新版本。 CouchDB不會像您期望的那樣丟掉丟失的版本,而是將其保存為文檔歷史記錄中的先前版本,以便您可以在需要時訪問它。這是自動且一致地發生的,因此兩個數據庫都將做出完全相同的選擇。

由您決定以對您的應用程序有意義的方式來處理衝突。您可以將選定的文檔版本保留在原位,還原為較舊的版本,或嘗試合併兩個版本並保存結果。

1.3.7 案例分析

朋友和同事Greg Borenstein建立了一個小型庫,用於將Songbird播放列表轉換為JSON對象,並決定將它們存儲在CouchDB中作為備份應用程序的一部分。完整的軟件使用CouchDB的MVCC和文檔修訂版,以確保在節點之間可靠地備份Songbird播放列表。

注意

Songbird是基於Mozilla XULRunner平臺的具有集成Web瀏覽器的免費軟件媒體播放器。 Songbird適用於Microsoft Windows,Apple Mac OS X,Solaris和Linux。

讓我們檢查Songbird備份應用程序的工作流程,首先是作為用戶從單臺計算機備份,然後使用Songbird在多臺計算機之間同步播放列表。我們將看到文檔修訂如何將本來很棘手的問題變成可以解決的問題。

第一次使用此備份應用程序時,我們會將播放列表饋入該應用程序並啟動備份。每個播放列表都將轉換為JSON對象,並傳遞到CouchDB數據庫。如圖5所示。備份到單個數據庫時,CouchDB會將每個播放列表的文檔ID和修訂版本保存到數據庫中。

「數據庫架構」Apache Couchdb 最終一致性

幾天後,我們發現我們的播放列表已更新,我們希望備份所做的更改。將播放列表饋入備份應用程序後,它會從CouchDB獲取最新版本以及相應的文檔修訂版。當應用程序移交新的播放列表文檔時,CouchDB要求文檔修訂包含在請求中。

然後,CouchDB確保請求中傳遞給它的文檔修訂與數據庫中保存的當前修訂匹配。因為CouchDB每次修改都會更新修訂,所以如果這兩個修改不同步,則表明在我們從數據庫請求文檔到發送更新之間,有人對文檔進行了更改。在其他人沒有先檢查那些更改的情況下對其進行更改通常是一個壞主意。

強迫客戶交出正確的文檔修訂版是CouchDB樂觀併發的核心。

我們有一臺筆記本電腦,希望與臺式機保持同步。在臺式機上播放所有播放列表後,第一步是“從備份還原”到筆記本電腦上。這是我們第一次這樣做,因此之後我們的筆記本電腦應保留桌面播放列表集合的精確副本。

在筆記本電腦上編輯我們的阿根廷探戈播放列表以添加一些我們購買的新歌曲後,我們要保存更改。備份應用程序替換了我們筆記本電腦CouchDB數據庫中的播放列表文檔,並生成了新的文檔修訂版。幾天後,我們記住了我們的新歌曲,並希望將播放列表複製到我們的臺式計算機上。如圖6所示,備份應用程序在兩個數據庫之間進行同步,將新文檔和新修訂版本複製到桌面CouchDB數據庫中。現在,兩個CouchDB數據庫都具有相同的文檔修訂版。

「數據庫架構」Apache Couchdb 最終一致性

因為CouchDB跟蹤文檔修訂,所以它確保僅當這些更新基於當前信息時這些更新才有效。 如果我們在同步之間對播放列表備份進行了修改,那麼事情就不會那麼順利。

我們在筆記本電腦上備份了一些更改,卻忘記了同步。 幾天後,我們正在臺式計算機上編輯播放列表,進行備份,並希望將其同步到筆記本電腦。 如圖7所示。兩個數據庫之間的同步衝突,當我們的備份應用程序嘗試在兩個數據庫之間複製時,CouchDB看到從臺式機發送的更改是對過時文檔的修改,並有幫助地通知我們 一直是一個衝突。

從應用程序的角度來看,從此錯誤中恢復很容易完成。 只需下載CouchDB的播放列表版本,即可提供合併更改或將本地修改保存到新播放列表中的機會。

「數據庫架構」Apache Couchdb 最終一致性

1.3.8 總結

CouchDB的設計大量借鑑了Web架構,並汲取了在該架構上部署大規模分佈式系統的經驗教訓。 通過了解這種體系結構為何能以這種方式工作,並通過學習發現可以輕鬆分發應用程序的哪些部分而不能輕鬆分發哪些部分,可以增強使用CouchDB或不使用CouchDB來設計分佈式和可伸縮應用程序的能力。

我們已經介紹了有關CouchDB一致性模型的主要問題,並暗示了在使用CouchDB而不是反對使用CouchDB時將獲得的一些好處。 但是有足夠的理論–讓我們開始並運行,看看大驚小怪的是什麼!

原文:https://docs.couchdb.org/en/stable/intro/consistency.html

本文:http://jiagoushi.pro/node/919

討論:請加入知識星球或者微信圈子【首席架構師圈】


分享到:


相關文章: