簡介
Berkeley DB是歷史悠久的嵌入式資料庫系統,主要套用在UNIX/LINUX作業系統上,其設計思想是簡單、小巧、可靠、高性能。
Berkeley DB (DB)是一個高性能的,嵌入資料庫編程庫,和C語言,C++,Java,Perl,Python,PHP,Tcl以及其他很多語言都有綁定。Berkeley DB可以保存任意類型的鍵/值對,而且可以為一個鍵保存多個數據。Berkeley DB可以支持數千的並發執行緒同時運算元據庫,支持最大256TB的數據,廣泛
用於各種作業系統包括大多數Unix類作業系統和Windows作業系統以及實時作業系統。
2.0版本或以上的Berkeley DB由Sleepycat Software公司開發,並使用基於自由軟體許可協定/私有許可協定的雙重授權方式提供[1],附有原始碼。開發者如果想把Berkeley DB嵌入在私有軟體內需要得到Sleepycat公司的許可,若將軟體同樣遵循GPL發布,則不需許可即可使用。而2.0版本以下的則使用BSD授權,可自由作商業用途。
Berkeley DB最初開發的目的是以新的HASH訪問算法來代替舊的hsearch函式和大量的dbm實現(如AT&T的dbm,Berkeley的 ndbm,GNU項目的gdbm),Berkeley DB的第一個發行版在1991年出現,當時還包含了B+樹數據訪問算法。在這以後,Berkeley DB得到了廣泛的套用,成為一款獨樹一幟的嵌入式資料庫系統。2006年Sleepycat公司被Oracle 公司收購,Berkeley DB成為Oracle資料庫家族的一員,Sleepycat原有開發者繼續在Oracle開發Berkeley DB,Oracle繼續原來的授權方式並且加大了對Berkeley DB的開發力度,繼續提升了Berkeley DB在軟體行業的聲譽。Berkeley DB的當前最新發行版本是 6.4.9。
值得注意的是DB是嵌入式資料庫系統,而不是常見的關係/對象型資料庫,對SQL語言不支持(目前已經支持SQL),也不提供資料庫常見的高級功能,如存儲過程,觸發器等。
結構
Berkeley DB以擁有比Microsoft SQL Server和Oracle等資料庫系統而言更簡單的體系結構而著稱。例如,它不支持網路訪問—程式通過進程內的API訪問資料庫。 他不支持其他的資料庫查詢語言,最新的版本已支持sql,不支持表結構和數據列。 訪問資料庫的程式自主決定數據如何儲存在記錄里,Berkeley DB不對記錄里的數據進行任何包裝,每個記錄有且只有兩部分:鍵、值,所以在Berkeley DB的背景下通常用key/data pair指代一個記錄。記錄和它的鍵都可以達到4G位元組的長度。
儘管架構很簡單,Berkeley DB卻支持很多高級的資料庫特性,比如ACID 資料庫事務處理,細粒度鎖,XA接口,熱備份以及同步複製。
DB中核心數據結構在使用前都要初始化,隨後可以調用結構中的函式(指針)完成各種操作,最後必須關閉數據結構。從設計思想的層面上看,這種設計方法是利用面向過程語言實現面對對象編程的一個典範。
Berkeley DB數據訪問算法
在資料庫領域中,數據訪問算法對應了數據在硬碟上的存儲格式和操作方法。在編寫應用程式時,選擇合適的算法可能會在運算速度上提高1個甚至多個數量級。大多數資料庫都選用B+樹算法,DB也不例外,同時還支持HASH算法、Recno算法和Queue算法。接下來,我們將討論這些算法的特點以及如何根據需要存儲數據的特點進行選擇。
B+樹算法
B+樹是一個平衡樹,關鍵字有序存儲,並且其結構能隨數據的插入和刪除進行動態調整。為了代碼的簡單,DB沒有實現對關鍵字的前綴碼壓縮。B+樹支持對數據查詢、插入、刪除的常數級速度。關鍵字可以為任意的數據結構.
HASH算法
DB中實際使用的是擴展線性HASH算法(extended linear hashing),可以根據HASH表的增長進行適當的調整。關鍵字可以為任意的數據結構。
要求每一個記錄都有一個邏輯紀錄號,邏輯紀錄號由算法本身生成。實際上,這和關係型資料庫中邏輯主鍵通常定義為int AUTO型是同一個概念。Recho建立在B+樹算法之上,提供了一個存儲有序數據的接口。記錄的長度可以為定長或不定長。 和Recno方式接近, 只不過記錄的長度為定長。數據以定長記錄方式存儲在佇列中,插入操作把記錄插入到佇列的尾部,相比之下插入速度是最快的。
對算法的選擇首先要看關鍵字的類型,如果為複雜類型,則只能選擇B+樹或HASH算法,如果關鍵字為邏輯記錄號,則應該選擇Recno或Queue算法。當工作集關鍵字有序時,B+樹算法比較合適;如果工作集比較大且基本上關鍵字為隨機分布時,選擇HASH算法。Queue算法只能存儲定長的記錄,在高的並發處理情況下,Queue算法效率較高;如果是其它情況,則選擇Recno算法,Recno算法把數據存儲為平面檔案格式。
Berkeley DB包含有與某些經典Unix資料庫編程庫兼容的接口,包括:dbm,ndbm和hsearch。
Berkeley DB的核心數據結構
資料庫環境句柄DB_ENV:每個DB_ENV相當於一個資料庫,它包含了資料庫全局信息,比如緩衝區大小、以及對事務、日誌、鎖等子系統的全局配置信息。
資料庫句柄結構DB:每個DB相當於關係資料庫的一個表,其中存儲了很多key/data pair。DB句柄代表了一個包含了若干描述資料庫表屬性的參數,如資料庫訪問方法類型、邏輯頁面大小、資料庫名稱等;同時,DB結構中包含了大量的資料庫處理函式指針,大多數形式為 (*dosomething)(DB *, arg1, arg2, …)。其中最重要的有open,close,put,get等函式。
資料庫記錄結構DBT:DB中的記錄由關鍵字和數據構成,關鍵字和數據都用結構DBT表示。實際上完全可以把關鍵字看成特殊的數據。結構中最重要的兩個欄位是 void * data和u_int32_t size,分別對應數據本身和數據的長度。
資料庫游標結構DBC:游標(cursor)是資料庫套用中常見概念,其本質上就是一個關於特定記錄的遍歷器。注意到DB支持多重記錄(duplicate records),即多條記錄有相同關鍵字,在對多重記錄的處理中,使用游標是最容易的方式。
資料庫環境句柄結構DB_ENV:環境在DB中屬於高級特性,本質上看,環境是多個資料庫的包裝器。當一個或多個資料庫在環境中打開後,環境可以為這些資料庫提供多種子系統服務,例如多線/進程處理支持、事務處理支持、高性能支持、日誌恢復支持等。
歷史
Berkeley DB函式館早期版本只有300K大小,但卻可管理高達256TB的數據,現在的最新版4.7也只有幾MB,既能在小型的嵌入式設備上使用,也可以在大型設備上管理重要的數據。它當前已經套用在Apache、MySQL、Sendmail、Subversion、OpenLDAP、Mozilla、Google等很多地方,而在MySQL資料庫中擔任的更是核心數據處理引擎,使MySQL成為一個小型的快速的關係資料庫,不過從MySQL5.1版本開始不再使用Berkeley DB,因為它已經被Oracle以10億美金之巨收歸囊中,而MySQL也已進了Sun的家門。
Berkeley DB對C、C++、Perl、Java、Python、Ruby、PHP等基本上所有的語言都提供了接口,對一條記錄只分為兩個欄位,一個為鍵,一個為值,鍵與值可以是任意的數據,並且可以長達4GB,它提供了四種數據存取算法:B+樹、Hash、Recno、Queue,根據不同數據類型,可以選擇適當的算法以達到最佳性能。
1991年,Berkeley DB的第一個版發行(Linux系統也是在這一年誕生)。
1992年,BSD UNIX第4.4發行版中包含了Berkeley DB1.85版。基本上認為這是Berkeley DB的第一個正式版。
1996年,Sleepycat軟體公司成立,提供對Berkeley DB的商業支持。
2006年,Sleepycat被Oracle收購,當前最新版本是4.7.25。
2009年,SUN被Oracle收購,不知道MySQL會不會再次啟用Berkeley DB。
特點
1. 訪問速度快
2. 省硬碟空間
Berkeley DB可以輕鬆支持上千個執行緒同時訪問資料庫,支持多進程、事務等特性。
Berkeley DB運行在大多數的作業系統中,例如大多數的UNIX系統, 和windows系統,以及實時作業系統。
Berkeley DB 還擁有對一些老的UNIX資料庫,例如dbm, ndbm und hsearch的兼容接口.
對於在java系統中的使用,Berkeley DB提供了一個壓縮成jar單個檔案的java版本。 這個版本可以運行在java虛擬機上使用,並且擁有和C語言版本相同的所有操作和功能。
Berkeley DB XML,是一個接口,通過它可以實現對XML數據存貯的支持。對XML數據的訪問,會使用相應的查詢語句如Xquery, Xpath。
Berkeley DB只支持單一的數據結構,它的所有數據包括兩個部分:key 和 data.
Berkeley DB原則上是為嵌入式資料庫設計的。
範例
函式使用
#include <db.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
/* DB的函式執行完成後,返回0代表成功,否則失敗 */
void print_error(int ret)
{
if(ret != 0)
printf("ERROR: %s/n",db_strerror(ret));
}
/* 數據結構DBT在使用前,應首先初始化,否則編譯可通過但運行時報參數錯誤 */
void init_DBT(DBT * key, DBT * data)
{
memset(key, 0, sizeof(DBT));
memset(data, 0, sizeof(DBT));
}
void main(void)
{
DB *dbp;
DBT key, data;
u_int32_t flags;
int ret;
char *fruit = "apple";
int number = 15;
typedef struct customer
{
int c_id;
char name[10];
char address[20];
int age;
} CUSTOMER;
CUSTOMER cust;
int key_cust_c_id = 1;
cust.c_id = 1;
strncpy(cust. name, "javer", 9);
strncpy(cust.address, "chengdu", 19);
cust.age = 32;
/* 首先創建資料庫句柄 */
ret = db_create(&dbp, NULL, 0);
print_error(ret);
/* 創建資料庫標誌 */
flags = DB_CREATE;
/* 創建一個名為single.db的資料庫,使用B+樹訪問算法,本段代碼演示對簡單數據類型的處理 */
ret = dbp->open(dbp, NULL, "single.db", NULL, DB_BTREE, flags, 0);
print_error(ret);
init_DBT(&key, &data);
/* 分別對關鍵字和數據賦值和規定長度 */
key.data = fruit;
key.size = strlen(fruit) + 1;
data.data = #ber;
data.size = sizeof(int);
/* 把記錄寫入資料庫中,不允許覆蓋關鍵字相同的記錄 */
ret = dbp->put(dbp, NULL, &key, &data,DB_NOOVERWRITE);
print_error(ret);
/* 手動把快取中的數據刷新到硬碟檔案中,實際上在關閉資料庫時,數據會被自動刷新 */
dbp->sync();
init_DBT(&key, &data);
key.data = fruit;
key.size = strlen(fruit) + 1;
/* 從資料庫中查詢關鍵字為apple的記錄 */
ret = dbp->get(dbp, NULL, &key, &data, 0);
print_error(ret);
/* 特別要注意數據結構DBT的欄位data為void *型,所以在對data賦值和取值時,要做必要的類型轉換。 */
printf("The number = %d/n", *(int*)(data.data));
if(dbp != NULL)
dbp->close(dbp, 0);
ret = db_create(&dbp, NULL, 0);
print_error(ret);
flags = DB_CREATE;
/* 創建一個名為complex.db的資料庫,使用HASH訪問算法,本段代碼演示對複雜數據結構的處理 */
ret = dbp->open(dbp, NULL, "complex.db", NULL, DB_HASH, flags, 0);
print_error(ret);
init_DBT(&key, &data);
key.size = sizeof(int);
key.data = &(cust.c_id);
data.size = sizeof(CUSTOMER);
data.data = &cust;
ret = dbp->put(dbp, NULL, &key, &data,DB_NOOVERWRITE);
print_error(ret);
memset(&cust, 0, sizeof(CUSTOMER));
key.size = sizeof(int);
key.data = &key_cust_c_id;
data.data = &cust;
data.ulen = sizeof(CUSTOMER);
data.flags = DB_DBT_USERMEM;
dbp->get(dbp, NULL, &key, &data, 0);
print_error(ret);
printf("c_id = %d name = %s address = %s age = %d/n",
cust.c_id, cust .name, cust.address, cust.age);
if(dbp != NULL)
dbp->close(dbp, 0);
}
游標使用
游標是依賴於資料庫句柄的,應用程式代碼框架如下:
/* 定義一個游標變數 */
DBC * cur;
/* 首先打開資料庫,再打開游標 */
dbp->open(dbp, ……);
dbp->cursor(dbp, NULL, &cur, 0);
/* do something with cursor */
/* 首先關閉,在關閉資料庫 */
cur->c_close(cur);
dbp->close(dbp, 0);
在游標打開後,可以以多種方式遍歷特定記錄。
Memset(&key, 0, sizeof(DBT));
Memset(&data, 0, sizeof(DBT));
/* 因為KEY和DATA為空,則游標遍歷整個資料庫記錄 */
While((ret = cur->c_get(cur, &key, &data, DB_NEXT)) == 0)
{
/* do something with key and data */
}
當想查詢特定關鍵字對應的記錄,則應對關鍵字賦值,並把cur->c_get()函式中標誌位設定為DB_SET。例如:
key.data = "xxxxx";
key.size = XXX;
While((ret = cur->c_get(cur, &key, &data, DB_SET)) == 0)
{
/* do something with key and data */
}
游標的作用還有很多,如查詢多重記錄,插入/修改/刪除記錄等。
環境使用
環境是DB資料庫的包裝器,提供多種高級功能。應用程式代碼框架如下:
/* 定義一個環境變數,並創建 */
DB_ENV *dbenv;
db_env_create(&dbenv, 0);
/* 在環境打開之前,可調用形式為dbenv->set_XXX()的若干函式設定環境 */
/* 通知DB使用Rijndael加密算法(參考資料4)對數據進行處理 */
dbenv->set_encrypt(dbenv, "encrypt_string", DB_ENCRYPT_AES);
/* 設定DB的快取為5M */
dbenv->set_cachesize(dbenv, 0, 5 * 1024 * 1024, 0);
/* 設定DB查找資料庫檔案的目錄 */
dbenv->set_data_dir(dbenv, "/usr/javer/work_db");
/* 打開資料庫環境,注意後四個標誌分別指示DB啟動日誌、加鎖、快取、事務處理子系統 */
dbenv->open(dbenv,home,DB_CREATE|DB_INIT_LOG|DB_INIT_LOCK| DB_INIT_MPOOL|DB_INIT_TXN, 0);
/* 在環境打開後,則可以打開若干個資料庫,所有資料庫的處理都在環境的控制和保護中。注意db_create函式的第二個參數是環境變數 */
db_create(&dbp1, dbenv, 0);
dbp1->open(dbp1, ……);
db_create(&dbp2, dbenv, 0);
dbp1->open(dbp2, ……);
/* do something with the database */
/* 最後首先關閉打開的資料庫,再關閉環境 */
dbp2->close(dbp2, 0);
dbp1->close(dbp1, 0);
dbenv->close(dbenv, 0);