Berkeley DB
Berkeley DB
Berkeley DB是歷史悠久的嵌入式資料庫系統,主要應用在UNIX/LINUX操作系統上,其設計思想是簡單、小巧、可靠、高性能。
Berkeley DB (DB)是一個高性能的,嵌入資料庫編程庫,和C語言,C++,Java,Perl,Python,PHP,Tcl以及其他很多語言都有綁定。Berkeley DB可以保存任意類型的鍵/值對,而且可以為一個鍵保存多個數據。Berkeley DB可以支持數千的併發線程同時操作資料庫,支持最大256TB的數據,廣泛
Berkeley DB
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位元組的長度。
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的兼容介面.
Berkeley DB XML,是一個介面,通過它可以實現對XML數據存貯的支持。對XML數據的訪問,會使用相應的查詢語句如Xquery, Xpath。
Berkeley DB只支持單一的數據結構,它的所有數據包括兩個部分:key 和 data.
Berkeley DB原則上是為嵌入式資料庫設計的。
#include
#include
#include
#include
void print_error(int ret)
{
if(ret != 0)
printf("ERROR: %s/n",db_strerror(ret));
}
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;
{
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;
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 = &number;
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;
ret = dbp->get(dbp, NULL, &key, &data, 0);
print_error(ret);
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;
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);
cur->c_close(cur);
dbp->close(dbp, 0);
在游標打開后,可以以多種方式遍歷特定記錄。
Memset(&key, 0, sizeof(DBT));
Memset(&data, 0, sizeof(DBT));
While((ret = cur->c_get(cur, &key, &data, DB_NEXT)) == 0)
{
}
當想查詢特定關鍵字對應的記錄,則應對關鍵字賦值,並把cur->c_get()函數中標誌位設置為DB_SET。例如:
key.data = "xxxxx";
key.size = XXX;
While((ret = cur->c_get(cur, &key, &data, DB_SET)) == 0)
{
}
游標的作用還有很多,如查詢多重記錄,插入/修改/刪除記錄等。
環境是DB資料庫的包裝器,提供多種高級功能。應用程序代碼框架如下:
DB_ENV *dbenv;
db_env_create(&dbenv, 0);
dbenv->set_encrypt(dbenv, "encrypt_string", DB_ENCRYPT_AES);
dbenv->set_cachesize(dbenv, 0, 5 * 1024 * 1024, 0);
dbenv->set_data_dir(dbenv, "/usr/javer/work_db");
dbenv->open(dbenv,home,DB_CREATE|DB_INIT_LOG|DB_INIT_LOCK| DB_INIT_MPOOL|DB_INIT_TXN, 0);
db_create(&dbp1, dbenv, 0);
dbp1->open(dbp1, ……);
db_create(&dbp2, dbenv, 0);
dbp1->open(dbp2, ……);
dbp2->close(dbp2, 0);
dbp1->close(dbp1, 0);
dbenv->close(dbenv, 0);