參考:<installdir>/docs/gsg/C/index.html 或對應的PDF文檔
1. BDB中的概念
說明:BDB是value/key資料庫,並不是關聯式資料庫。
下面是BDB中的幾個重要概念:
records: 邏輯上,每一個記錄表示了資料庫的一個入口。每一個record包含了兩個資訊:key和data。
access methods: 存取方法,Berkeley給使用者提供了多種存取方法,供使用者根據自己的需求來選擇合適的方法來組織資料庫的資料。
一般選擇存取方法有兩個原則:首先以何種方式來適用Key;其次是要擷取的效能。要選擇某種特定的訪問方式必須在資料庫建立的時候選擇,然後以後通過所有BerkeleyDB提供的API進行的是所有的操作都是基於此存取方法的。此後存取方法對使用者來說可視為透明。
BDB的access methods主要有:BTree,Hash,Queue,Recno。
傳回值:BDB中的API總是在成功的時候返回0,如果遇到失敗,會返回一個非0值。
2. 資料庫操作
在BDB中,一個資料庫是records的集合。Recards,按照順序,包含了key/data的索引值對。概念上,可以把一個資料庫看成是包含兩列的表格,其中第一列包含一個key,第二列包含data資料。key和data都是通過DBT結構體管理的。
(1) 開啟資料庫
要開啟資料庫,首先需要使用db_creaet()函數初始化一個DB控制代碼(handle)。一旦初始化了DB handle,就可以使用它的open()方法來開啟資料庫了。
需要說明的是,預設情況下,如果一個DB不存在,open不會自己建立資料庫。可以給open()指定DB_CREATE標誌。
(2) 關閉資料庫
在使用完資料庫的時候,需要關閉它。使用close()方法即可。在關閉一個資料庫之後,除非再次開啟,否則不能使用該資料庫。而且,建議在關閉資料庫之前,關閉所有開啟的遊標cursor。在關閉資料庫的時候,仍然active的遊標會導致不可預料的結果,特別是如果這些遊標正在對資料庫進行寫操作。通常,你需要保證你在關閉資料庫之前,所有的資料庫訪問都已經完成。
在關閉一個資料庫的最後一個開啟的控制代碼的時候,預設它的cache會被寫入到磁碟。所以通常不需要手動寫cache到磁碟。但是,也可以使用DB->sync()方法手動寫cache到磁碟。
下面是開啟和關閉資料庫的代碼:
// gcc 1.c -Iinclude -ldb -Llib#include <db.h>#include <stdio.h>#include <stdlib.h>DB *dbp;/* DB structure handle */u_int32_t flags;/* database open flags */int ret;/* function return value */void openDB() {/* Initialize the structure. This* database is not opened in an environment,* so the environment pointer is NULL. */ret = db_create(&dbp, NULL, 0);if (ret != 0) {/* Error handling goes here */printf("db_create error.\n");exit(0);}/* Database open flags */flags = DB_CREATE; /* If the database does not exist, create it.*//* open the database */ret = dbp->open(dbp,/* DB structure pointer */NULL,/* Transaction pointer */"my_db.db",/* On-disk file that holds the database. */NULL,/* Optional logical database name */DB_BTREE,/* Database access method */flags,/* Open flags */0);/* File mode (using defaults) */if (ret != 0) {/* Error handling goes here */printf("DB open error.\n");exit(0);}printf("DB open ok.\n");}void closeDB() {/* When we're done with the database, close it. */if (dbp != NULL) {ret=dbp->close(dbp, 0);if (ret != 0) {/* Error handling goes here */printf("DB close error.\n");} else {printf("DB close ok.\n");}} else {printf("handle is NULL, do not use it to close DB.\n");}}int main() {openDB();closeDB();return 0;}
(3) 資料庫開啟標誌
下面介紹幾個資料庫開啟的基本標誌,需要注意的是,這不包括所有的標誌,具體完整的標誌列表,參考API文檔。下面的標誌是對於入門需要瞭解的,而且是針對單線程資料庫程式的。
如果需要使用多個標誌,可以使用OR操作符(|)。
DB_CREATE: 如果資料庫當前不存在,就建立它。預設情況下,如果資料庫不存在,開啟會失敗。
DB_EXCL: 如果資料庫存在,那麼開啟資料庫就會失敗。需要說明的是,這個標誌只有在DB_CREATE被使用的時候才有意義。
DB_RDONLY: 以唯讀方式開啟資料庫。導致寫入操作會失敗。
DB_TRUNCATE: 物理上刪除包含該資料庫的磁碟檔案,導致DB刪除一個檔案中包含的所有資料庫。
(4) 管理資料庫的方法
下面的DB方法,只有在你管理DB資料庫的時候才會有用。
DB->get_open_flags(): 返回當前的開啟標誌。如果資料庫沒有開啟,使用這個方法返回錯誤。
DB->remove(): 刪除指定的資料庫。如果database參數沒有指定,那麼整個資料庫檔案都會被刪除。注意:如果一個資料庫有一個開啟的控制代碼,不要刪除這個資料庫。
DB->rename(): 重新命名資料庫。
(5) 錯誤報表函數
為了簡化錯誤報表和處理,DB結構體提供了幾個有用的方法。
set_errcall()
set_errfile()
set_errpfx()
err()
errx()
(6) 在enviroments中使用資料庫
3. 資料庫記錄
DB記錄包含兩個部分,一個key和一些資料。key和其對應的資料data是包含在一個DBT結構中的。因此,訪問一個DB的記錄,需要兩個這樣的結構體,一個是key,一個是data。
DBT結構體提供了一個void*域,用於指向你自己的資料data,另一個域是資料長度。因此,它們能被用於儲存任何內容,從基本的資料類型到複雜的結構體,只要這些資料是在一個連續的記憶體塊中。
下面介紹DBT的使用,以及如何從資料庫儲存和擷取key/value對。
(1) 使用資料庫記錄
每一個資料庫記錄由兩個DBT結構體組成,一個是key,另一個是data。
要儲存一個資料庫的記錄,如果其key和data是基礎資料型別 (Elementary Data Type)(int,float等),或者是基礎資料型別 (Elementary Data Type)的數組,我們只需要指定記憶體的位置和其長度,如下:
DBT key, data;
int iKey=10;
char* iData="this is data";
/* zero out the DBTs before using them. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
key.data=&iKey;
key.size=sizeof(int);
data.data=iData;
data.size=strlen(iData)+1;
要擷取一個記錄,只需要將DBT中返回的void*給合適的變數即可。
(2) 讀寫資料庫記錄
在讀寫資料庫記錄的時候,需要注意資料庫是否支援重複的記錄。如果多個資料庫的記錄的key相同,它們會被認為是重複的記錄。具有同樣的key的資料集合稱之為重複集(duplicates set)。在DB中,對於重複集,一個給定的key只會被儲存一次。
預設情況下,DB資料庫不支援重複記錄。如果資料庫支援重複記錄,就需要使用cursor遊標來訪問重複集中的所有記錄了。
DB提供了兩種基本的機制來儲存和擷取資料庫的key/data對:
DBT->put()和DBT->get()提供了對於非重複資料的簡單的訪問;
cursor遊標提供了很多方法來儲存和擷取資料。
下面介紹put和get的使用。
寫記錄到資料庫:
DBT->put()
put函數提供了一個參數是flags,用於控制DB的資料庫寫的行為。一個比較常用的flag是DB_NOOVERWRITE,這個標誌禁用了覆寫資料庫中已有的記錄。如果提供的key在資料庫中已經存在,那麼put方法就會返回DB_KEYEXIST,即使資料庫支援重複記錄。
從資料庫讀記錄:
DBT->get()
如果資料庫支援重複記錄,那麼預設情況下這個方法只返回第一個記錄。所以,對於支援重複記錄的資料庫,通常使用cursor來擷取記錄。(當然,也可以使用整體的get,即使用DB_MULTIPLE標誌給get方法,這樣它會返回所有的記錄,但是要考慮如果記錄太多,記憶體塊需要很大)。
預設情況下,get()方法返回的是key匹配的第一條記錄,如果資料庫支援重複記錄,可以通過DB_GET_BOTH標誌,會導致get()返回key和data都匹配的第一條記錄。
如果指定的key和/或data在資料庫中不存在,那麼get返回返回DB_NOTFOUND。
刪除記錄:
DB->del()
用於刪除資料庫中的記錄。如果資料庫支援重複記錄,那麼所有相關的資料都會被刪除。如果是從重複記錄中刪除一條,那麼使用cursor遊標。
如果要刪除資料庫中的所有記錄,可以使用DB->truncate()。
(3) 在DB中使用結構體
前面說過,DB雖然只有一個key和data,但是data是可以儲存任意多個資料的,可以通過結構體來實現儲存多個資料。只要結構體的域沒有指標,那麼可以類似於單一資料型別一樣的進行儲存和讀取。只需要設定其地址為DBT的data,並設定DBT的size即可。
但是,更多的情況,結構體可能需要指標,比如如果是一個很大的字串,可能需要動態分配記憶體。對於結構體的欲包含指標的情況,處理起來稍微複雜一點,因為DBT必須要求其data的內容是在連續的記憶體上的,如果是指標,那麼就指向了堆記憶體,顯然不連續,所以,解決方案是通過把結構體的所有域的資料拷貝到一個臨時的結構體中,然後進行資料庫的讀寫。
4.