/*
* 這個例子程序是Berkeley DB的示例程序之一(DB/example_cxx/AccessMethod.cpp),
* 它演示瞭如何使用Berkeley DB的基本功能,包括打開一個數據庫,存入若干個
* key/data pair,然後遍歷數據庫中的數據,最後關閉數據庫。
*
* 原始代碼中有一些英文註釋,但是對於初學者還是不夠詳細,我沒有刪除原來
* 的註釋,而且添加了針對每一個Berkeley DB操作的更加詳細的說明,請參考。
*
* 代碼的非關鍵部分都已刪除,所以這裏的內容
* 無法直接編譯運行。可以直接編譯運行的版本我會放到空間的附件中。
*
*
* 用詞約定:
* 本文提到的“數據庫”是指Berkeley DB的database,相當於關係數據庫的一個表。
* 一個數據庫當中保存着很多個key/data pair,相當於關係數據庫的一個表當中
* 保存着很多條記錄。也就是說一個key/data pair相當於關係數據庫的一條記錄。
* 而數據庫環境(DbEnv)是指Berkeley Db的執行環境,相當於一個關係數據庫管理系統。
*/
/* 測試用例類聲明 */
class AccessExample
{
public:
AccessExample();
void run(bool removeExistingDatabase, const char *fileName);
private:
// no need for copy and assignment
AccessExample(const AccessExample &);
void operator = (const AccessExample &);
};
/*
* 這個例子程序演示瞭如何使用Berkeley DB的基本功能,包括打開一個數據庫,存入
* 若干個key/data pair,然後遍歷數據庫中的數據,最後關閉數據庫。
*/
int
main(int argc, char *argv[])
{
// Use a try block just to report any errors.
// An alternate approach to using exceptions is to
// use error models (see DbEnv::set_error_model()) so
// that error codes are returned for all Berkeley DB methods.
//
try {
AccessExample app;
app.run((bool)(rflag == 1 ? true : false), database);
return (EXIT_SUCCESS);
}
catch (DbException &dbe) {
cerr << "AccessExample: " << dbe.what() << "/n";
return (EXIT_FAILURE);
}
}
void AccessExample::run(bool removeExistingDatabase, const char *fileName)
{
// Remove the previous database.
if (removeExistingDatabase)
(void)remove(fileName);
// Create the database object.
// There is no environment for this simple example.
// 這裏我們沒有指定顯式的environment,BerkeleyDB會在內部創建一個專門供
// 這個數據庫使用的environment. 同時,第二個參數表明,當出現錯誤後,
// Berkeley DB會拋出異常。
Db db(0, 0);
// 當出現運行錯誤後,錯誤信息寫入cerr流。
//
// Berkeley DB在調試模式下,可以可選地輸出非常豐富的錯誤信息和其他運行期信息,
// 大大簡化了調試過程。比如,你可以讓Berkeley DB把錯誤信息寫到一個文件、
// 一個c++ io流中,或者調用用戶註冊的回調函數由用戶自己處理錯誤信息,
// 以及在錯誤信息中前綴某些自定義信息,等等。
//
// 關於錯誤報告和調試的文檔:
// http://www.oracle.com/technology/documentation/berkeley-db/db/ref/debug/runtime.html
// http://www.oracle.com/technology/documentation/berkeley-db/db/ref/am_misc/error.html
// http://www.oracle.com/technology/documentation/berkeley-db/db/ref/env/error.html
// http://www.oracle.com/technology/documentation/berkeley-db/db/ref/program/errorret.html
db.set_error_stream(&cerr);
// 在每個錯誤信息前綴 "AccessExample"
db.set_errpfx("AccessExample");
// 設置數據庫頁的size爲1024字節。數據庫頁的設置會在較大程度上影響數據庫的性能,
// 這裏有關於頁設置的說明:
// http://www.oracle.com/technology/documentation/berkeley-db/db/ref/am_conf/pagesize.html
db.set_pagesize(1024); /* Page size: 1K. */
// 設置cache的大小,數據庫的頁調入內存後,就放在cache當中,也就是說,cache
// 就是存放調入內存的數據庫頁面的,如果整個數據庫中每一個頁面都可以調入
// 內存長期存放,那麼數據庫的速度自然很快,所以,cache確實是越大越好的,
// 當它大於所有數據庫頁面所佔空間後,對性能就沒什麼影響了。
//
// 設置cache size的指導 :
// http://www.oracle.com/technology/documentation/berkeley-db/db/api_cxx/frame.html
//
// 如果cache填滿了,數據庫會淘汰不用的頁面,若這樣的頁面有改動,會寫回
// 數據庫文件。騰出空間後再調入目標頁面。這涉及很多磁盤操作,所以數據庫
// 操作會突然變慢很多,應用程序的性能就會偶爾
// 發生短暫地下降。爲了避免這種性能抖動,你可以在一個單獨運行的線程當中,
// 定期淘汰頁面騰出cache的空間,保證cache總是有一定的空間可用,
// 這叫做memory trickle。
//
// 方法見此鏈接:
// http://www.oracle.com/technology/documentation/berkeley-db/db/api_cxx/frame.html
db.set_cachesize(0, 32 * 1024, 0);
// 打開數據庫。這個函數的文檔 :
// http://www.oracle.com/technology/documentation/berkeley-db/db/api_cxx/frame.html
//
// 這裏創建的是一個btree數據庫。Berkeley DB支持四種Access Method,也就是
// 數據庫文件內部組織數據的方式,包括btree, hash, queue和record number,
// 分別使用DB_BTREE, DB_HASH, DB_QUEUE和DB_RECNO來指定。
//
// 每一種access method都有自己的優點和缺點,適用於某種需求。所以你應該根據自己
// 的應用程序的數據存儲需求,數據訪問方式來決定使用哪種access method.
// 關於如何選擇合適的access method:
// http://www.oracle.com/technology/documentation/berkeley-db/db/ref/am_conf/select.html
db.open(NULL, fileName, NULL, DB_BTREE, DB_CREATE, 0664);
//
// Insert records into the database, where the key is the user
// input and the data is the user input in reverse order.
//
char buf[1024], rbuf[1024];
char *p, *t;
int ret;
u_int32_t len;
// 循環獲取用戶輸入的字符串str, 把str逆序得到str1, 然後存儲(str, str1)
// 作爲一個key/data pair.
for (;;) {
cout << "input> ";
cout.flush();
cin.getline(buf, sizeof(buf));
if (cin.eof())
break;
if ((len = (u_int32_t)strlen(buf)) <= 0)
continue;
for (t = rbuf, p = buf + (len - 1); p >= buf;)
*t++ = *p--;
*t++ = '/0';
// Dbt類用於存儲用戶的一個數據項的信息。一個key/data pair是一次存儲
// 操作的單位,相當於關係數據庫
// 的一個行(row),key是這行的主鍵,data是其他各個字段的集合。
//
// Berkeley DB不對data做細分和理解,
// 應用程序自然知道自己存儲的數據的結構和意義。
//
// key/data pair中的key和data都是一個數據項,它們各需要一個Dbt對象來描述。
// 由於Berkeley DB存儲的是字節串,
// 它不理會數據的更多意義,比如類型等,所關心數據項信息只包括:
// 字節串起始地址,長度,內存管理的flags(約定了在讀和寫一個key/data pair
// 時,由誰分配和釋放Dbt::data所指向的內存),
// 以及用於做分塊讀取的字段,這個後面再講。
//
// 這裏我們創建的兩個對象key和data分別代表要存儲的一個key/data pair
// 的key和data。我們把字節串的起始地址和長度傳給了它們,Berkeley DB即
// 可得到這兩個字節串。
Dbt key(buf, len + 1);
Dbt data(rbuf, len + 1);
// 存儲這個key/data pair。DB_NOOVERWRITE 表示如果已經有了這個key,
// 那麼不要覆蓋那個key/data pair,
// 而是返回錯誤。Db::put的第四個參數允許你設置若干種flag,來控制插入
// 一個key/data pair的行爲。
//
// Db::put的文檔:
// http://www.oracle.com/technology/documentation/berkeley-db/db/api_cxx/frame.html
ret = db.put(0, &key, &data, DB_NOOVERWRITE);
// 如上所述,由於不覆蓋已有的相同key的key/data pair,如果這樣的key
// 真的存在,Db::put就會返回DB_KEYEXIST。
if (ret == DB_KEYEXIST) {
cout << "Key " << buf << " already exists./n";
}
}
cout << "/n";
// We put a try block around this section of code
// to ensure that our database is properly closed
// in the event of an error.
//
try {
// Acquire a cursor for the table.
Dbc *dbcp;
// 創建一個遊標來遍歷數據庫。遊標的作用與ODBC/JDBC等的遊標的意義相同,
// 它指向一個key/data pair,可以
// 更改、讀取、刪除它所指向的key/data pair,同時具有遊標穩定性--
// 它所指向的key/data pair不會被其他遊標修改或者刪除。
db.cursor(NULL, &dbcp, 0);
// Walk through the table, printing the key/data pairs.
// 此處我們是要使用遊標dbcp遍歷整個數據庫,所以我們不需要指定key的值,
// key和data都是用來存儲返回結果的。
// 並且,在這種默認情況下,用於保存返回結果的內存有Berkeley DB
// 負責分配和釋放。
// 你也可以通過指定其他的flag,並且自己分配並且/或者自己釋放存儲着
// 結果字節串的內存。
//
// 類Dbt的文檔:
// http://www.oracle.com/technology/documentation/berkeley-db/db/api_cxx/frame.html
// 裏面有所有的flags,以及幾種Dbt的內存管理方式。
Dbt key;
Dbt data;
// 循環獲取下一條key/data pair。當沒有更多的key/data pair時候,
// Dbc::get會返回非0值。一個遊標dbcp在創建之初,
// 並不指向任何一條key/data pair,而第一次調用Dbc::get並且傳入
// DB_NEXT flag,就會使得dbcp位於第一個key/data pair。
// Dbc::get的文檔:
// http://www.oracle.com/technology/documentation/berkeley-db/db/api_cxx/frame.html
//
// key/data pair的順序是由數據庫的存取方式定義的。比如對於btree這種
// 存取方式,key的順序由它們的大小關係決定。
// 你可以配置key的比較函數來自定義key的比較方式,見文檔:
// http://www.oracle.com/technology/documentation/berkeley-db/db/api_cxx/frame.html
while (dbcp->get(&key, &data, DB_NEXT) == 0) {
// 獲取Dbt中的數據,也就是字節串的首地址。由於key和data對象
// 使用了默認的flags,所以它們所引用的內存由Berkeley Db負責
// 分配和回收。你也可以使用其他的內存管理方式。
// 詳情Dbt的文檔。
char *key_string = (char *)key.get_data();
char *data_string = (char *)data.get_data();
cout << key_string << " : " << data_string << "/n";
}
// 關閉遊標。 一定別忘了做這個,並且儘早關閉遊標。
// 這是因爲遊標穩定性導致遊標所引用的
// 頁面被鎖定,使用同一個數據庫的其他進程或者線程無法訪問這些頁面。
dbcp->close();
}
catch (DbException &dbe) {
cerr << "AccessExample: " << dbe.what() << "/n";
}
// 關閉數據庫。
db.close(0);
}
Berkeley DB示例程序詳解 (1)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.