您的位置:首页 > 其它

Berkeley DB示例程序详解 (1)

2009-02-26 14:24 429 查看
/*

* 这个例子程序是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);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: