Berkeley DB示例程序詳解 (1)

 /*
* 這個例子程序是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);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章