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