鹼與酸

開放

6.1 縮寫之戰:BASE vs. 酸

“在化學中,pH 測量水溶液的相對酸度。pH 範圍從 0(強酸性物質)到 14(強鹼性物質);25°C 的純水的 pH 值為 7,呈中性。

數據工程師用這個比喻來比較數據庫的交易可靠性。”

可能的想法是:pH 值越高,即 數據庫越接近“鹼性”(“BASE”),交易越不可靠。

流行的關係型數據庫,如MySQL,就是在ACID的基礎上出現的。但是在過去的十年裡,所謂的 NoSQL 數據庫,在這個名稱下結合了幾種非常不同類型的數據庫,在沒有 ACID 的情況下也做得很好。事實上,有大量使用 NoSQL 數據庫的開發人員根本不關心事務及其可靠性。讓我們看看他們是否正確。

你不能籠統地談論 NoSQL 數據庫,因為它只是一個很好的抽象。NoSQL 數據庫在數據存儲子系統的設計上各不相同,甚至在數據模型上也各不相同:NoSQL 既是面向文檔的 CouchDB,又是面向圖形的 Neo4J。但是如果我們在事務的上下文中談論它們,它們往往在一件事上是相似的:它們提供有限版本的原子性和隔離性,因此不提供 ACID 保證。要理解這意味著什麼,讓我們回答這個問題:如果不提供 ACID,它們提供什麼?沒有什麼?

並不真地。畢竟,它們和關係數據庫一樣,也需要用漂亮的包裝來推銷自己。他們想出了自己的“化學”縮寫 - BASE。

6.2 BASE 作為對手

在這裡,我不會按字母順序排列,但我將從基本術語 - 一致性開始。我將不得不平衡你的識別效果,因為這種一致性與 ACID 的一致性關係不大。術語一致性的問題在於它在太多上下文中使用。但是這種一致性有更廣泛的使用背景,實際上這正是討論分佈式系統時所討論的一致性。

我們上面談到的關係數據庫提供了不同級別的事務隔離,其中最嚴格的可以確保一個事務無法看到另一個事務所做的無效更改。如果你站在商店的收銀台,這時租金從你的賬戶中被提取,但是租金轉賬交易失敗,你的賬戶恢復到原來的價值(這筆錢是not debited),那麼你在結賬時的支付交易不會讓所有人注意到這些手勢——畢竟那筆交易從來沒有經過,基於交易隔離的要求,它的臨時變化是無法被其他交易注意到的。

許多 NoSQL 數據庫放棄了隔離保證並提供“最終一致性”,您最終會看到有效數據,但您的事務有可能讀取無效值 - 即臨時的,或部分更新的,或過時的。讀取時數據可能會在“惰性”模式下變得一致(“lazily at read time”)。

NoSQL 被設想為用於實時分析的數據庫,為了獲得更快的速度,他們犧牲了一致性。Eric Brewer,也是創造 BASE 一詞的人,制定了所謂的“CAP 定理”,根據該定理:

對於分佈式計算的任何實現,可以提供不超過以下三個屬性中的兩個:

  • 數據一致性(consistency)——不同節點(實例)上的數據不會相互矛盾;
  • 可用性(availability)——對分佈式系統的任何請求都以正確的響應結束,但不保證所有系統節點的響應都相同;
  • 分區容忍度(partition tolerance)——即使節點之間沒有連接,它們也繼續彼此獨立地工作。

如果您想對 CAP 有一個非常簡單的解釋,那麼這裡就是了。

有意見認為 CAP 定理不起作用,一般表述過於抽象。無論如何,NoSQL 數據庫通常拒絕 CAP 定理上下文中的一致性,它描述了以下情況:數據已在具有多個實例的集群中更新,但更改尚未在所有實例上同步。請記住,我在上面提到了 DynamoDB 示例,它告訴我:您的更改變得持久 - 這是給您的 HTTP 200 - 但我只在 10 秒後看到更改?開發人員日常生活中的另一個例子是 DNS,即域名系統。如果有人不知道,那麼這正是將http(s)地址翻譯成IP地址的“字典”。

更新的 DNS 記錄根據緩存間隔設置傳播到服務器 - 因此更新不會立即引起注意。好吧,關係數據庫集群(比如 MySQL)可能會發生類似的時間不一致(即最終一致性)——畢竟,這種一致性與 ACID 的一致性無關。因此,重要的是要理解,從這個意義上說,當涉及到集群中的多個實例時,SQL 和 NoSQL 數據庫不太可能有很大的不同。

此外,端到端的一致性可能意味著寫入請求會亂序:即所有數據都會被寫入,但最終接收到的值不會是寫入隊列中的最後一個。。

由於端到端的一致性模型,非 ACID NoSQL 數據庫具有所謂的“軟狀態”,這意味著即使沒有輸入,系統狀態也會隨時間變化。但是這樣的系統努力提供更大的可訪問性。提供 100% 的可用性並不是一項微不足道的任務,因此我們談論的是“基本可用性”。這三個概念:“基本可用”、“軟狀態”(“軟狀態”)和“最終一致性”構成了首字母縮寫詞 BASE。

老實說,在我看來,BASE 的概念比 ACID 更像是一個空洞的營銷包裝——因為它沒有提供任何新內容,也沒有以任何方式表徵數據庫。而給某些數據庫附加標籤(ACID、BASE、CAP)只會讓開發人員感到困惑。我決定無論如何都要向你介紹這個術語,因為在研究數據庫時很難繞過它,但既然你知道它是什麼,我希望你盡快忘記它。讓我們回到隔離的概念。

6.3 所以 BASE 數據庫根本不符合 ACID 標準?

本質上,ACID 數據庫與非 ACID 的不同之處在於非 ACID 實際上放棄了隔離。理解這一點很重要。但更重要的是閱讀數據庫文檔並像 Hermitage 項目的人那樣測試它們。這個或那個數據庫的創建者如何稱呼他們的創意並不重要——ACID 或 BASE,CAP 或非 CAP。重要的是這個或那個數據庫到底提供了什麼。

如果數據庫的創建者聲稱它提供 ACID 保證,那麼這可能是有原因的,但建議您自己進行測試以了解是否如此以及在何種程度上如此。如果他們聲明他們的數據庫不提供此類保證,那麼這可能意味著以下內容:

  • 數據庫不提供原子性保證。雖然一些 NoSQL 數據庫為原子操作提供了單獨的 API(例如 DynamoDB);

  • 數據庫不提供隔離保證。這可能意味著,例如,數據庫不會按照寫入數據的順序寫入數據。

至於持久性保證,很多數據庫為了性能都在這一點上做出了妥協。寫入磁盤是一個太長的操作,有幾種方法可以解決這個問題。我不想過多地探討數據庫理論,但為了讓您大致了解應該如何看待,我將籠統地描述不同的數據庫如何解決持久性問題。

要比較不同的數據庫,除其他外,您需要知道特定數據庫的數據存儲和檢索子系統下的數據結構是什麼。簡而言之:不同的數據庫有不同的索引實現——即組織對數據的訪問。其中一些可以讓您更快地寫入數據,而另一些則可以讓您更快地讀取數據。但是也不能籠統的說有的數據結構讓耐久性變高或者變低。

6.4 不同的數據庫如何索引數據,以及這如何影響持久性等

存儲和檢索數據有兩種主要方法。

最簡單的保存數據的方式就是以類日誌的方式在文件末尾添加操作(即總是會發生追加操作):不管我們是想添加、更改還是刪除數據——都CRUD 操作只是寫入日誌。搜索日誌效率低下,這就是索引的用武之地 - 一種特殊的數據結構,用於存儲有關數據存儲位置的元數據。最簡單的日誌索引策略是跟踪鍵和值的哈希映射。這些值將引用寫入文件內部的數據的字節偏移量,這就是日誌(log)並存儲在磁盤上。這種數據結構完全存儲在內存中,而數據本身在磁盤上,稱為 LSM 樹(日誌結構合併)。

您可能想知道:如果我們一直將我們的操作寫入日誌,那麼它會增長過快嗎?是的,因此發明了壓縮技術,它以某種週期性“清理”數據,即只為每個鍵留下最相關的值,或刪除它。而如果我們在磁盤上有多個日誌,而是多個,並且它們都是排序的,那麼我們將得到一個名為 SSTable(“排序字符串表”)的新數據結構,這無疑會提高我們的性能。如果我們想在內存中排序,我們會得到一個類似的結構——所謂的 MemTable,但隨之而來的問題是,如果發生致命的數據庫崩潰,那麼最後寫入的數據(位於 MemTable 中,但尚未寫入到磁盤)都丟失了。實際上,

另一種索引方法是基於 B 樹(“B 樹”)。在 B 樹中,數據以固定大小的頁面寫入磁盤。這些數據塊的大小通常約為 4 KB,並且具有按鍵排序的鍵值對。一個 B 樹節點就像一個包含一系列頁面鏈接的數組。最大限度。數組中的鏈接數稱為分支因子。每個頁面範圍都是另一個 B 樹節點,帶有指向其他頁面範圍的鏈接。

最終,在工作表級別,您會找到單獨的頁面。這個想法類似於低級編程語言中的指針,只是這些頁面引用存儲在磁盤上而不是內存中。當數據庫中發生 INSERT 和 DELETE 時,某個節點可以分裂成兩個子樹以匹配分支因子。如果數據庫在過程中由於任何原因出現故障,則數據的完整性可能會受到損害。為了防止這種情況發生,使用 B 樹的數據庫維護了一個“預寫日誌”或 WAL,其中記錄了每個事務。此 WAL 用於在 B 樹損壞時恢復其狀態。似乎這就是使使用 B 樹的數據庫在持久性方面更好的原因。但是基於 LSM 的數據庫也可以維護一個文件,該文件本質上執行與 WAL 相同的功能。因此,我將重複我已經說過的話,也許不止一次:了解您選擇的數據庫的運行機制。

然而,關於 B 樹可以肯定的是,它們有利於事務性:每個鍵只出現在索引中的一個位置,而日誌存儲子系統可以在不同的分片中擁有相同鍵的多個副本(例如,直到執行下一次壓縮)。

但是,索引的設計直接影響到數據庫的性能。對於 LSM 樹,寫入磁盤是順序的,而 B 樹會導致多次隨機磁盤訪問,因此 LSM 的寫操作比 B 樹更快。這種差異對於磁性硬盤驅動器 (HDD) 尤其重要,其中順序寫入比隨機寫入快得多。LSM 樹上的讀取速度較慢,因為您必須查看處於不同壓縮階段的多個不同數據結構和 SS 表。更詳細地說,它看起來像這樣。如果我們使用 LSM 進行簡單的數據庫查詢,我們將首先在 MemTable 中查找鍵。如果不存在,我們查看最近的 SSTable;如果不存在,那麼我們查看倒數第二個 SSTable,依此類推。如果請求的密鑰不存在,那麼使用 LSM 我們將最後知道這一點。LSM 樹用於,例如:LevelDB、RocksDB、Cassandra 和 HBase。

我描述得如此詳細,以便您了解在選擇數據庫時,您需要考慮許多不同的事情:例如,您希望寫入數據還是讀取數據更多。而且我還沒有提到數據模型的差異(你是否需要遍歷數據,就像圖形模型允許的那樣?你的數據中不同單元之間是否存在任何關係——那麼關係數據庫會來拯救?),和 2 種類型的數據模式——寫入時(如在許多 NoSQL 中)和讀取時(如在關係中)。

如果我們回到持久性方面,那麼結論會是這樣的:任何一個寫入磁盤的數據庫,不管索引機制如何,都可以為你的數據的持久性提供很好的保證,但是你需要針對每個具體的數據庫進行處理,它到底提供了什麼。

6.5 內存數據庫如何工作

順便說一下,除了寫入磁盤的數據庫之外,還有主要使用 RAM 的所謂“內存中”數據庫。簡而言之,內存數據庫通常為了更快的寫入和讀取速度而提供較低的持久性,但這可能適用於某些應用程序。

事實上,RAM 內存長期以來一直比磁盤更昂貴,但最近它開始迅速變得更便宜,這催生了一種新型數據庫——考慮到從 RAM 讀取和寫入數據的速度,這是合乎邏輯的。但你會問:這些數據庫的數據安全性如何?再次在這裡,您需要查看實現的細節。通常,此類數據庫的開發人員提供以下機制:

  • 您可以使用電池供電的 RAM;
  • 可以將更改日誌寫入磁盤(類似於上面提到的 WAL),但不能寫入數據本身;
  • 您可以定期將數據庫狀態的副本寫入磁盤(在不使用其他選項的情況下,不能提供保證,但只會提高耐用性);
  • 您可以將 RAM 的狀態復製到其他機器。

例如,主要用作消息隊列或緩存的內存中 Redis 數據庫缺乏 ACID 的持久性:它不能保證成功執行的命令將存儲在磁盤上,因為 Redis 會將數據刷新到磁盤(如果您啟用持久性)僅以定期異步方式進行。

然而,這對所有應用程序來說並不重要:我發現了一個 EtherPad 合作在線編輯器的示例,它每 1-2 秒刷新一次,用戶可能會丟失幾個字母或一個單詞,這並不重要。否則,由於內存數據庫的優點在於它們提供了難以用磁盤索引實現的數據模型,因此可以使用 Redis 來實現事務——它的優先級隊列允許你這樣做。

留言
  • 受歡迎
你必須登入才能留言
此頁面尚無留言