瞭解 Oracle Berkeley DB 能夠爲您的應用程序帶來 NoSQL 優點的緣由及方式。

將 Oracle Berkeley DB 用做 NoSQL 數據存儲

做者:Shashank Tiwarihtml

2011 年 2 月發佈java

「NoSQL」是在開發人員、架構師甚至技術經理中新流行的一個詞彙。儘管這個術語最近很流行,但使人驚訝的是,它並無一個廣泛承認的定義。git

一般來講,任何非 RDBMS 且遵循無模式結構的數據庫通常都不能徹底支持 ACID 事務,而且因高可用性的承諾以及在橫向伸縮環境中支持大型數據集而廣泛被歸類爲「NoSQL 數據存儲」。鑑於這些共同特徵(與傳統的 RDBMS 的特徵造成鮮明對比),有人提議非關係(或者簡稱爲 NonRel)是比 NoSQL 更爲恰當的術語。github

儘管定義衝突仍然存在,但不少人已經意識到將 NoSQL 加到其應用程序體系中的好處。其餘人正保持密切關注並評估 NoSQL 是否適合他們。算法

NoSQL 做爲一個類別的發展還致使了大量新數據存儲的出現。其中某些新的 NoSQL 產品擅長持久保存像 JSON 這樣的文檔,某些按照列家族存儲排序,其餘的則持久保存分佈式鍵值對。儘管更新的產品使人興奮而且提供了不少好用的功能,但一些現有產品也在奮起直追履行 新的承諾。sql

Oracle Berkeley DB 就是這樣一個數據存儲。在文本中,我將解釋並說明爲何能夠將 Berkeley DB 做爲 NoSQL 解決方案包括在體系中以及具體實現方式。本文重點關注 Berkeley DB 圍繞 NoSQL 的特性,所以不會詳盡涵蓋 Berkeley DB 的全部功能和特性。數據庫

Berkeley DB 要素

基本上,鍵值存儲 Berkeley DB 有三種不一樣風格:數組

  • Berkeley DB — 用 C 編寫的鍵值存儲。(Berkeley DB 官方文檔使用術語鍵-數據 代替鍵值。)這是「經典」風格。緩存

  • Berkeley DB Java 版 (JE) — 用 Java 從新編寫的鍵值存儲。能夠輕鬆包含在 Java 堆棧中。網絡

  • Berkeley DB XML — 用 C 編寫,此版本將鍵值存儲進行包裝,使其行爲相似於一個已創建索引而且通過優化的 XML 存儲系統。

(注意:儘管本文沒有明確涉及 Berkeley DB JE 或 Berkeley DB XML,可是包括了一些使用 Java API 和基於 Java 的持久性框架來講明 Berkeley DB 功能的示例。)

Berkeley DB 的核心可能很簡單,能夠將它配置爲提供並行非阻塞訪問或支持事務,橫向擴展爲一個主從副本的高可用集羣或者以多種其餘方式橫向擴展。

Berkeley DB 是一個純存儲引擎,不對鍵值對的隱式模式或結構作任何假設。所以,Berkeley DB 輕鬆容許在底層鍵值存儲上實現更高級別的 API、查詢和建模抽象。這有助於快速高效地存儲應用程序特定數據,而不會產生將其轉換爲抽象數據格式的開銷。這種簡單卻精緻的設計所提供的靈活性可以在 Berkeley DB 中同時存儲結構化和半結構化數據。

Berkeley DB 可做爲內存中存儲來運行,以保存少許數據,也可經過快速的內存中緩存配置爲大型數據存儲。在更高級別抽象(稱做環境)的幫助下,能夠在一個物理安裝中配置多個數據庫。一個環境能夠有多個數據庫。您須要打開一個環境,而後打開一個數據庫,向其中寫入數據或者從中讀取數據。建議您在完成交互後關閉數據庫和環境,從而以最佳方式使用資源。

數據庫中的每一項都是一個鍵值對。鍵一般是惟一的,可是您能夠有重複的項。值是經過鍵來訪問的。能夠更新檢索值並將其保存回到數據庫。經過遊標對多個值進行訪問和迭代。遊標使您能夠循環遍歷值的集合以及同時操縱整個值集合。另外,還支持事務和併發訪問。

鍵值對的鍵幾乎老是充當創建索引的主鍵。值中的其餘屬性可充當次索引。在輔助數據庫中單獨維護次索引。所以,具備鍵值對的主要數據庫有時候也被稱做主數據庫。

Berkeley DB 做爲一個進程中數據存儲運行,所以在使用 C、C++、C#、Java 或腳本語言 API 從相應程序中訪問它時,您會以靜態或動態方式連接到它。

簡要介紹以後,下面將就 Berkeley DB 圍繞 NoSQL 的特性進行介紹。

靈活的模式

NoSQL 存儲的第一個優點是其對定義明確的數據庫模式的寬鬆態度。咱們來看看 Berkeley DB 如何實現此特性。

爲了理解 Berkeley DB 的功能,建議您試用一下。所以,建議將 Berkeley DB 和 Berkeley DB JE 下載並安裝到您的計算機上,這樣您能親自嘗試一些示例並跟隨本文中其他例證的操做。此處在 線提供了下載連接和安裝說明。(在本文中,我使用 --enable-java、--enable-sql 和 --prefix=/usr/local 對 Berkeley DB 進行了編譯。)與存儲、訪問機制和 API 有關的基本概念在 Berkeley DB 和 Berkeley DB JE 之間沒有太大區別,所以我後面涉及到的大部份內容一樣適用於這二者。

除了數據項必須是鍵值對集合以外,Berkeley DB 自己對數據項的限制很是少。這就使得應用程序能夠靈活地使用 Berkeley DB 管理各類格式的數據,包括 SQL、XML 和 Java 對象。您能夠經過基礎 API、SQL API、Java Collections API 以及 Java Direct Persistence Layer (DPL) 訪問 Berkeley DB 中的數據。它容許幾種不一樣存儲配置:B 樹、散列、隊列和 Recno。(Berkeley DB 文檔將不一樣存儲機制稱做「訪問方法」。散列、隊列和 Recno 訪問方法僅在 Berkeley DB 中可用,在 Berkeley DB JE 或 Berkeley DB XML 中不可用。)

您能夠根據具體用例來選擇訪問機制和存儲配置。選擇特定的訪問方法和存儲配置會影響模式。要了解您的選擇所形成的影響,您須要先了解您所選的內容。我接下來要談到訪問方法和存儲配置。

使用基礎 API

基 礎 API 是低級別 API,使您能夠存儲、檢索和更新數據(即鍵值對)。這種 API 在幾種不一樣語言綁定之間是相似的。所以,C、C++ 和 Java 的基礎 API 是徹底相同的。另外一方面,DPL 和 Java Collections API 僅做爲抽象在 Java API 中提供。

基 礎 API 可放置、獲取和刪除鍵值對。鍵和值均爲字節數組。在存儲全部鍵和數據值以前,會將其序列化爲字節數組。您可使用 Java 的內置序列化程序或 Berkeley DB 的 BIND API 將各類數據類型序列化爲字節數組。Java 的內置序列化程序一般執行速度較慢,所以用戶一定更喜歡 BIND API。(jvm-serializers 項目對各類替代序列化程序進行基準測試,是用於在 JVM 的不一樣序列化機制之間分析相對性能的一個很好的參照點。)BIND API 可經過每一個序列化類來避免冗餘存儲類信息,將該信息放在單獨的數據庫中。經過編寫您本身的自定義字節組綁定來提升 BIND API 性能,您能夠潛在地提升速度。

做爲一個基本示例,您能夠定義以下數據值:

import java.io.Serializable;
public class DataValue implements Serializable {
    private long prop1;
    private double prop2;

    DataValue() { 
      prop1 = 0;
      prop2 = 0.0;
    }

    public void setProp1(long data) {
      prop1 = data;
    }
    
    public long getProp1() {
      return prop1;
    }
    
    public void setProp2(double data) {
      prop2 = data;
    }
    
    public double getProp2() {
      return prop2;
    }
}


如今,您可使用兩個數據庫來存儲此數據值,一個數據庫存儲帶有鍵的值,另外一個數據庫存儲類信息。

使用四個不一樣步驟來存儲數據:

  1. 首先,除了用於存儲鍵值對的數據庫以外的另外一個數據庫配置爲存儲類數據,以下所示:

    Database aClassDB = new Database("classDB", null, aDbConfig);
  2. 而後,將一個類目錄實例化,以下所示:
    StoredClassCatalog storedClassCatalog = new StoredClassCatalog(aClassDb);
  3. 創建一個串行條目綁定,以下所示:
    EntryBinding binding = new SerialBinding(storedClassCatalog, DataValue.class);
  4. 最終,DataValue 實例以下所示:
    DataValue val = new DataValue();
    val.setProp1(123456789L);
    val.setProp2(1234.56789); 


    使用您剛建立的綁定映射到 Berkeley DB DatabaseEntry(充當鍵和值的包裝器),以下所示:

    DatabaseEntry deKey = new DatabaseEntry(aKey.getBytes("UTF-8"));
    DatabaseEntry deVal = new DatabaseEntry();
    binding.objectToEntry(val, deVal);


如今,您能夠將鍵值對放入 Berkeley DB 中。

基礎 API 支持 put 和 get 方法的幾種變體,以容許或不容許重複項和覆蓋。(該示例以及本文都不是爲了要教您有關如何使用基礎 API 的詳細語法或語義,所以我將不會涉及更多細節;請參閱這裏的文檔)。一個要點是,基礎 API 容許就存儲、檢索和刪除鍵值對進行低級操做和自定義序列化。

若是偏向於使用更高級的 API 與 Berkeley DB 進行交互,那麼您應使用 DPL。

使用 DPL

直 接持久層 (DPL) 提供了熟悉的 Java 持久性框架語義來操縱對象。您能夠將 Berkeley DB 視做一個實體存儲,對象在其中持久保存,並可對其中的對象進行檢索以便更新和刪除。DPL 使用批註將類標記爲 @Entity。使用實體進行存儲的相關聯的類被註釋爲 @Persistent。特定屬性或變量能夠註釋爲 @PrimaryKey 和 @SecondaryKey。一個簡單的實體可能以下所示:

@Entity
public class AnEntity {

    @PrimaryKey
    private int myPrimaryKey;

    @SecondaryKey(relate=ONE_TO_ONE)
    private String mySecondaryKey;
    ...
} 


DPL 將類定義用做定義明確的模式。經過基礎 API,咱們知道 Berkeley DB 不要求必須符合模式。但對於某些用例,正式的實體定義頗有幫助並可爲數據建模提供結構化方法。

存儲配置

正如前面所提到的,能夠經過四種不一樣類型的數據結構存儲鍵值對:B 樹、散列、隊列和 Recno。咱們來看看它們的效果如何。

  • B 樹。須要對 B 樹進行一些簡要介紹,但若是您須要查看其定義,請閱讀有關 B 樹的 Wikipedia 頁面 http://en.wikipedia.org/wiki/B-tree。 這是一種平衡的樹型數據結構,保證其元素通過排序並容許快速順序訪問、插入和刪除。鍵和值能夠爲任意數據類型。在 Berkeley DB 中,B 樹訪問方法容許重複項。若是您須要用複雜數據類型做爲鍵,這是一個不錯的選擇。若是數據訪問模式致使訪問相鄰的記錄,這也是一種很好的選擇。B 樹保存了大量元數據,能夠高效地執行。大部分 Berkeley DB 應用程序使用 B 樹存儲配置。
  • 散列。與 B 樹相似,散列也容許以複雜類型做爲鍵。與 B 樹相比,散列具備更加線性化的結構。Berkeley DB 散列結構容許重複項。

    盡 管 B 樹和散列均支持複雜鍵,可是當數據集遠超過可用內存大小時,散列數據庫的性能一般優於 B 樹。這是由於 B 樹比散列保存更多的元數據,更大的數據集意味着 B 樹元數據可能沒法存儲在內存中緩存內。在這種極端狀況下,B 樹元數據以及實際數據記錄自己一般必須取自文件,而這會致使每一個操做有多個 I/0。散列訪問方法旨在最大程度減小訪問數據記錄所需的 I/O 數量,所以在這些極端狀況下,性能可能會優於 B 樹。

  • 隊列。隊列是一組順序存儲的固定 長度記錄。鍵被限制爲整數類型的邏輯記錄編號。記錄是按順序追加,容許極快寫入。若是您對 Apache Cassandra 經過向日志進行追加的快速寫入印象深入,那麼請嘗試採用隊列訪問方法的 Berkeley DB,您必定不會失望。這些方法還容許從隊列的頭有效地讀取和更新。隊列還支持行級鎖定。這樣即便是在併發處理的狀況下,也能保證有效的事務完整性。
  • Recno。Recno 與隊列相似,可是容許可變長度的記錄。與隊列相似,reco 鍵也被限制爲整數。

不 同配置使您能夠在集合中存儲任意類型的數據。與 NoSQL 相似,沒有固定模式(除了您的模型實施的模式)。在極端狀況中,您能夠在集合中針對兩個鍵分別存儲不一樣的值類型。值類型能夠是複雜類,就參數而言,能夠表 示 JSON 文檔、複雜數據結構或結構化數據集。真正的惟一限制是,值應該序列化爲字節數組。單個鍵或單個值最大可達 4GB。

次索引的出現容許根據值屬性進行篩選。主數據庫不會以表格格式來存儲數據,所以不會爲稀疏數據集存儲非現有屬性。若是鍵值對缺乏用於建立索引的屬性,次索引會跳過全部此類鍵值對。通常來講,這種存儲方式既緊湊又高效。

對事務的支持

Berkeley DB 是一個很是靈活的數據庫,能夠打開和關閉許多特性。Berkeley DB 能夠在不支持事務的狀況下運行,也能夠編譯爲支持 ACID 事務完整性。也許,Berkeley DB 的可塑性使其成爲很是適合許多狀況的數據存儲。在典型的 NoSQL 數據存儲中,對事務完整性的支持最差。在不指望 ACID 事務合規性的具備較高可用性的系統中,Berkeley DB 能夠關閉事務,像典型的 NoSQL 產品那樣工做。可是在其餘系統中,它可能很靈活而且支持事務完整性。

儘管我並不打算涉及有關事務的細節,但值得注意的是,像 傳統 RDBMS 系統同樣,支持事務的 Berkeley DB 容許定義事務邊界。一旦提交,數據會持久保存到磁盤。爲提升性能,您可使用非持久性提交,這會將寫操做提交到內存中日誌文件,隨後與底層文件系統進行同 步。還支持隔離級別和鎖定機制。

在數據庫關閉以前,同步操做可保證持久文件副本在系統中具備最新的內存中信息。這種同步操做與 Berkeley DB 的事務恢復子系統的組合(假定您已啓用了事務)可確保數據庫始終返回到一致的事務狀態,即便是在應用程序或系統發生故障時。

大型數據集

理 論上,Berkeley DB 具備 256TB 的上限,但實際上,一般受運行 Berkeley DB 的計算機的大小限制。截至撰寫本文時,Berkeley DB 未證明可在分佈式文件系統的幫助下支持跨多臺計算機的極大文件。(可藉助 Hadoop 分佈式文件系統 (HDFS) 等分佈式文件系統的幫助管理超過單個節點大小的文件。)Berkeley DB 在本地文件系統上的性能優於在網絡文件系統上的性能。更準確地說,Berkeley DB 依賴文件系統的 POSIX 兼容屬性。例如,當 Berkeley DB 調用 fsync() 而且文件系統返回時,Berkeley DB 假定數據已寫入到持久介質。出於性能緣由,分佈式文件系統一般不保證自始至終完成向持久介質的寫入。

所支持的最大 B 樹深度爲 255。鍵和值的長度一般受可用內存的限制。

橫向擴展

Berkeley DB 複製遵循主從模式。在此類模式中,有一個主節點和多個從屬節點(或副本)。可是,主節點的選擇不是靜態的,而且不建議手動選擇。複製集羣中的全部參與節點 都要經歷一個選舉過程以選出主節點。具備最新日誌記錄的參與節點將成爲獲勝者。若是具備綁定,那麼優先級用於選擇主節點。選舉過程基於行業標準的符合 Paxos 的算法

複製具備不少好處,包括:

  • 提升讀性能 — 可從多個副本節點中讀取數據極大提升了讀性能。

  • 提升可靠性 — 有了副本實例,就能夠在發生節點故障和數據損壞時提供更好的故障轉移選擇。

  • 提升持久性 — 您能夠放寬對主節點的持久性保證以免過多地對磁盤進行寫入操做,這一般須要昂貴的 I/O。在集羣環境中,經過將寫入提交到多個節點(即便未寫入到磁盤)這一事實加強了持久性。

  • 提升可用性 — 因爲有多個節點而且對磁盤進行異步寫入,即便在主節點負載太高的狀況下,副本節點仍可繼續提供服務。

總結

毫無疑問,Berkeley DB 做爲一個強健、可伸縮的 NoSQL 鍵值存儲很是合格;Amazon 的 DynamoProject VoldemortMemcacheDBGenieDB 使用 Berkeley DB 做爲底層存儲就是支持這一觀點的進一步證據。圍繞 Berkeley DB 性能一直存在一些恐懼,尤爲是下面這兩個在線發佈的比較基準測試:

可是,不少運行中的系統證實了 Berkeley DB 的強大。其中許多系統通過了仔細的調整和應用程序編碼改進,已經得到了出色的可伸縮性、吞吐量和可靠性結果。效法這些系統,Berkeley DB 無疑可用做可伸縮的 NoSQL 解決方案。


Shashank TiwariTreasury of Ideas(一 家技術驅動的創新和價值優化公司)的創始人兼 CEO。做爲一名經驗豐富的軟件開發人員和架構師,他精通大量技術。他是國際承認的演講者、做者和導師。做爲數種 JCP (Java Community Process) 規範的專家組成員,他一直積極參與規劃 Java 的將來。他還表明了 NoSQL 和雲計算領域的心聲,是 RIA 社區公認的專家。

相關文章
相關標籤/搜索