Berkeley DB for C
簡單介紹Brekeley DB
打開databases
在開打數據庫前必須通過db_create()方法來初始化一個db句柄, 然後你可以通過他的open方法來打開一個數據庫
示例:
#include <db.h> //必要的包含文件
...
DB *dbp; /* 數據庫結構句柄 */
u_int32_t flags; /* 數據庫打開標誌 */
int ret; /* 用來接收通過db_create來創建的數據庫是否成功 */
/* 初始化結構體
* 因爲數據庫沒有在環境中打開
* 所以環境指針爲null. */
ret = db_create(&dbp, NULL, 0);
if (ret != 0) {
/* 錯誤處理 */
}
/* 數據庫打開標誌 */
flags = DB_CREATE; /* 如果數據庫不存在的化則創建一個新的*/
/* 打開數據庫 */
ret = dbp->open(dbp, /* 數據庫結構指針 */
NULL, /* 事務處理指針 */
"my_db.db", /* 數據庫名 */
NULL, /* Optional logical database name */
DB_BTREE, /* 數據庫訪問方式 */
flags, /* 打開標識 */
0); /* 文件模式 (使用默認的) */
if (ret != 0) {
/* 錯誤處理 */
}
關閉databases
一旦你使用完畢某個數據庫,你必須通過DB->close()來關閉他。除非你再次打開他否則關閉後的數據庫是不能在使用的。在關閉數據庫之前請確保與之相關的遊標先關閉,否則會有很多想不到的後果。關閉數據庫後很多緩存在內存中的數據會被同步到你的硬盤中去,而且你也可以隨時通過DB->sync()方法來隨時同步數據庫。
使用示例:
#include <db.h>
...
DB *dbp; /* 數據庫句柄 */
...
/*
* 在這裏進行一些數據庫有關的操作
*/
...
/* 當你不在使用他的時候關閉他. */
if (dbp != NULL)
dbp->close(dbp, 0);
數據庫打開表識(flags)
如果在打開數據庫的時候使用多個標識則可以通過按位或來添加多個標示
示例:
u_int32_t open_flags = DB_CREATE | DB_EXCL;
l 標示說明:
DB_CREATE:當數據不存在的時候創建一個新的
DB_EXCL:創建專用數據庫,如果數據庫已經存在則將會打開失敗,這個標識只能與DB_CREATE聯合使用
DB_RDONLY:以只讀方式打開數據庫,所有的寫操作都被禁止。
DB_TRUNCATE:打開數據庫的同時清空數據庫中原有的記錄。
下面這些方法可能會對你管理數據庫有用
l DB->get_open_flags():返回當前的數據庫打開標識,如果在打開數據庫前調用此方法會出錯。
使用示例:
#include <db.h>
...
DB *dbp;
u_int32_t open_flags;
/* Database open and subsequent operations omitted for clarity */
dbp->get_open_flags(dbp, &open_flags);
l DB->remove()
如果一個數據庫已經不再需要,則可以使用這個方法來刪除。
示例:
#include <db.h>
...
DB *dbp;
/* Database open and subsequent operations omitted for clarity */
dbp->remove(dbp, /* 數據庫句柄 */
"mydb.db", /* 要移除的數據庫 */
NULL, /* Database to remove. This is
* NULL so the entire file is
* removed. */
0); /* Flags. None used. */
l DB->rename()
給目標數據庫改名,不要試圖給一個已經打開了的數據庫改名,那樣會出錯的。
使用示例:
#include <db.h>
...
DB *dbp;
/* Database open and subsequent operations omitted for clarity */
dbp->rename(dbp, /* Database pointer */
"mydb.db", /* 要被改名的數據庫 */
NULL, /* Database to rename. This is
* NULL so the entire file is
* renamed. */
"newdb.db", /* 新的數據庫名 */
0);
l 與錯誤報告有關的幾個函數
set_errcall():設置出錯的時候調用的函數
set_errfile():設置出錯的時候 把錯誤信息寫進去的文件指針(file *)
set_errpfx():設置有數據庫產生的錯誤類型
err():產生一個錯誤,如果指定了錯誤的回調函數或文件指針則把錯誤發送給相應的處理方法或文件,否則發送到標準出錯。
errx():與err()雷同
除此之外你可以通過db_strerror()來直接的返回錯誤代號。
使用示例:
/*
* Function called to handle any database error messages
* issued by DB.
*/
void
my_error_handler(const char *error_prefix, char *msg)
{
/*
* 相關的錯誤處理
*/
}
註冊錯誤回調函數的方法如下
#include <db.h>
#include <stdio.h>
...
DB *dbp;
int ret;
/*
* 爲錯誤創建一個初始化的環境
*/
ret = db_create(&dbp, NULL, 0);
if (ret != 0) {
fprintf(stderr, "%s: %s/n", "my_program",
db_strerror(ret));//直接輸出錯誤信息
return(ret);
}
/* 設置回調函數 */
dbp->set_errcall(dbp, my_error_handler);
dbp->set_errpfx(dbp, "my_example_program");//設置回調前綴
發出錯誤信息示例:
ret = dbp->open(dbp,
NULL,
"mydb.db",
NULL,
DB_BTREE,
DB_CREATE,
0);
if (ret != 0) {
dbp->err(dbp, ret,
"Database open failed: %s", "mydb.db");//發送錯誤信息
return(ret);
}
數據庫環境管理
要想設置數據庫環境,首先你需要創建一個環境句柄並且打開他,在打開的時候你必須確保存在指定的目錄,同樣你頁可以設置環境所使用的內存cache.
使用示例:
#include <db.h>
...
DB_ENV *myEnv; /* db環境結構句柄 */
DB *dbp; /* db結構句柄 */
u_int32_t db_flags; /* 數據庫打開標識 */
u_int32_t env_flags; /* 環境打開標識 */
int ret; /* 用來存儲打開結果(成功還是失敗) */
/*
創建一個數據庫環境
*/
ret = db_env_create(&myEnv, 0);
if (ret != 0) {
//錯誤處理
return -1;
}
/* 打開環境. */
env_flags = DB_CREATE | /* 如果環境不存在則創建一個新的*/
DB_INIT_MPOOL; /* 初始化內存大小. */
ret = myEnv->open(myEnv, /* 數據庫環境指針 */
"/export1/testEnv", /* 環境目錄 */
env_flags, /* 打開標識 */
0); /* File mode (default) */
if (ret != 0) {
fprintf(stderr, "Environment open failed: %s", db_strerror(ret));
//錯誤處理
return -1;
}
一但打開一個數據庫環境後,你就可以通過他來打開數據庫。通常情況下數據庫默認存儲在環境的根目錄或其相對路徑
使用示例:
/*
* 創建數據庫
*/
ret = db_create(&dbp, myEnv, 0);
if (ret != 0) {
/* Error handling goes here */
}
/* 數據庫打開標誌 */
db_flags = DB_CREATE; /* If the database does not exist,
* create it.*/
/* 打開數據庫 */
ret = dbp->open(dbp, /* db結構指針 */
NULL, /* 事務處理指針 */
"my_db.db", /* 數據庫名 */
NULL, /* Optional logical database name */
DB_BTREE, /* 數據庫訪問方式 */
db_flags, /* 打開標識符 */
0); /* File mode (using defaults) */
if (ret != 0) {
/* 錯誤處理 */
}
當你使用完一個環境後必須關閉他,請確保在關閉一個數據庫環境前先關閉所有數據庫。
使用示例:
/*
* 關閉數據庫和環境
*/
if (dbp != NULL) {
dbp->close(dbp, 0);
}
if (myEnv != NULL) {
myEnv->close(myEnv, 0);
}
使用記錄
每條數據庫記錄是由兩個DBT結構體所組成,一個代表鍵名(key),一個代表鍵值(value);
爲了存儲數據我們只需要拿一個指針指向這段數據的首地址,並且標識出這段數據的長度就行了。具體使用示例如下:
DBT key, data; //分別定義key和value
float money = 122.45;
char *description = "Grocery bill.";
/* 開始使用DBT的時候給DBT清0 */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
key.data = &money;
key.size = sizeof(float);
data.data = description;
data.size = strlen(description) + 1;
爲了能夠得到這條記錄,可以通過void指針指向DBT.
某些系統的float和struct類型的內存對齊方式比較特殊,爲了兼容性,應該使用Dbt的set_ulen函數設置內存長度,並且用set_flag(DB_DBT_USERMEM)函數註明使用BDB的內存處理方式.
只有在獲取數據的時候,才需要ulen的值。get()操作後,DBT的sizefield會設置爲data的實際大小。可以通過設置flag爲0,獲得data的實際大小。如果設置了flag爲DB_DBT_USERMEM,那麼必須使用try-catch模板包圍get()操作,如果返回的data長度超過了ulen的大小,會issues DBException,打印出來即爲 DB_BUFFER_SMALL 錯誤。
1. 讀和些記錄
在讀和寫數據庫記錄的時候需要了解掉你的數據庫是否支持多重記錄,如果有兩條或多條數據對應同一個key值則表示數據庫支持多條記錄。默認情況下數據庫是不支持多從記錄的,在遊標那節我們將會介紹怎樣通過遊標來操作多條記錄。
DB提供了兩個基本的方法來存儲和檢索數據。
a) DBT->put()和DBT->get()這兩個方法提供了檢索和存儲非多重記錄的簡單方法。
b) 當然也可以通過遊標的方式來獲取,遊標將在後面的章節做介紹。
通過DBT->put向數據庫中寫記錄的時候,你也許能夠用到DB_NOOVERWRITE這個標識,他表示如果記錄已經在數據庫中存在,則返回DB_KEYEXIST給你,即使數據庫支持多重記錄他也是會返回的。使用示例:
#include <db.h>
#include <string.h>
...
char *description = "Grocery bill.";
DBT key, data;
DB *my_database;
int ret;
float money;
/* Database open omitted for clarity */
money = 122.45;
/* DBT數據清0. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
key.data = &money;
key.size = sizeof(float);
data.data = description;
data.size = strlen(description) +1;
//從這裏開始存儲數據
ret = my_database->put(my_database, NULL, &key, &data, DB_NOOVERWRITE);
if (ret == DB_KEYEXIST) {//如果之前已經存儲過這條記錄的,則進行相關的處理
my_database->err(my_database, ret,
"Put failed because key %f already exists", money);
}
你可以通過DB->get()方法來從數據庫中讀取記錄。如果你的數據庫支持多重記錄則會返回第一條記錄。
#include <db.h>
#include <string.h>
...
DBT key, data;
DB *my_database;
float money;
char *description[DESCRIPTION_SIZE + 1];
/* Database open omitted for clarity */
money = 122.45;
/* 清0. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
key.data = &money;
key.ulen = sizeof(float);
data.set_data(&description);//設置數據
data.set_ulen(DESCRIPTION_SIZE + 1);
data.set_flags(DB_DBT_USERMEM);
my_database->get(my_database, NULL, &key, &data, 0);
/*
* Description is set into the memory that we supplied.
*/
同樣的你也可以通過DB->del()方法來刪除一條記錄,如果key對應多重記錄則所有的記錄都會被刪除,同樣的你可以通過DB->truncate()方法來清空所有的記錄。
使用示例:
#include <db.h>
#include <string.h>
...
DBT key;
DB *my_database;
float money = 122.45;
/* Database open omitted for clarity */
/* Zero out the DBTs before using them. */
memset(&key, 0, sizeof(DBT));
key.data = &money;
key.size = sizeof(float);
my_database->del(my_database, NULL, &key, 0);
BDB與硬盤IO之間的同步方式採用的是異步的方式,也就是說對數據進行寫操作的時候不是立即dump到本地硬盤中去的,當關閉數據庫的時候所有的數據會立即dump到本地硬盤中去的,當然你也可以通過調用DB->sync().來隨時同步,但頻繁的IO肯定會對效率大打則扣。如果在向硬盤同步數據完畢前發生了錯誤,數據就有可能丟失。可以通過DB->verify()這個方法來檢查數據庫,必要的時候也可以通過db_dump方法來挽留儘可能多的數據。
今天先到這裏,明天繼續,有點困了。