参考:<installdir>/docs/gsg/C/index.html 或对应的PDF文档
1. BDB中的概念
说明:BDB是value/key数据库,并不是关系数据库。
下面是BDB中的几个重要概念:
records: 逻辑上,每一个记录表示了数据库的一个入口。每一个record包含了两个信息:key和data。
access methods: 访问方法,Berkeley给用户提供了多种访问方法,供用户根据自己的需求来选择合适的方法来组织数据库的数据。
一般选择访问方法有两个原则:首先以何种方式来适用Key;其次是要获取的性能。要选择某种特定的访问方式必须在数据库创建的时候选择,然后以后通过所有BerkeleyDB提供的API进行的是所有的操作都是基于此访问方法的。此后访问方法对用户来说可视为透明。
BDB的access methods主要有:BTree,Hash,Queue,Recno。
返回值:BDB中的API总是在成功的时候返回0,如果遇到失败,会返回一个非0值。
2. 数据库操作
在BDB中,一个数据库是records的集合。Recards,按照顺序,包含了key/data的键值对。概念上,可以把一个数据库看成是包含两列的表格,其中第一列包含一个key,第二列包含data数据。key和data都是通过DBT结构体管理的。
(1) 打开数据库
要打开数据库,首先需要使用db_creaet()函数初始化一个DB句柄(handle)。一旦初始化了DB handle,就可以使用它的open()方法来打开数据库了。
需要说明的是,默认情况下,如果一个DB不存在,open不会自己创建数据库。可以给open()指定DB_CREATE标志。
(2) 关闭数据库
在使用完数据库的时候,需要关闭它。使用close()方法即可。在关闭一个数据库之后,除非再次打开,否则不能使用该数据库。而且,建议在关闭数据库之前,关闭所有打开的游标cursor。在关闭数据库的时候,仍然active的游标会导致不可预料的结果,特别是如果这些游标正在对数据库进行写操作。通常,你需要保证你在关闭数据库之前,所有的数据库访问都已经完成。
在关闭一个数据库的最后一个打开的句柄的时候,默认它的cache会被写入到磁盘。所以通常不需要手动写cache到磁盘。但是,也可以使用DB->sync()方法手动写cache到磁盘。
下面是打开和关闭数据库的代码:
// gcc 1.c -Iinclude -ldb -Llib
#include <db.h>
#include <stdio.h>
#include <stdlib.h>
DB *dbp; /* DB structure handle */
u_int32_t flags; /* database open flags */
int ret; /* function return value */
void openDB() {
/* Initialize the structure. This
* database is not opened in an environment,
* so the environment pointer is NULL. */
ret = db_create(&dbp, NULL, 0);
if (ret != 0) {
/* Error handling goes here */
printf("db_create error.\n");
exit(0);
}
/* Database open flags */
flags = DB_CREATE; /* If the database does not exist, create it.*/
/* open the database */
ret = dbp->open(dbp, /* DB structure pointer */
NULL, /* Transaction pointer */
"my_db.db", /* On-disk file that holds the database. */
NULL, /* Optional logical database name */
DB_BTREE, /* Database access method */
flags, /* Open flags */
0); /* File mode (using defaults) */
if (ret != 0) {
/* Error handling goes here */
printf("DB open error.\n");
exit(0);
}
printf("DB open ok.\n");
}
void closeDB() {
/* When we're done with the database, close it. */
if (dbp != NULL) {
ret=dbp->close(dbp, 0);
if (ret != 0) {
/* Error handling goes here */
printf("DB close error.\n");
} else {
printf("DB close ok.\n");
}
} else {
printf("handle is NULL, do not use it to close DB.\n");
}
}
int main() {
openDB();
closeDB();
return 0;
}
(3) 数据库打开标志
下面介绍几个数据库打开的基本标志,需要注意的是,这不包括所有的标志,具体完整的标志列表,参考API文档。下面的标志是对于入门需要了解的,而且是针对 单线程数据库程序的。
如果需要使用多个标志,可以使用OR操作符(|)。
DB_CREATE: 如果数据库当前不存在,就创建它。默认情况下,如果数据库不存在,打开会失败。
DB_EXCL: 如果数据库存在,那么打开数据库就会失败。需要说明的是,这个标志只有在DB_CREATE被使用的时候才有意义。
DB_RDONLY: 以只读方式打开数据库。导致写入操作会失败。
DB_TRUNCATE: 物理上删除包含该数据库的磁盘文件,导致DB删除一个文件中包含的所有数据库。
(4) 管理数据库的方法
下面的DB方法,只有在你管理DB数据库的时候才会有用。
DB->get_open_flags(): 返回当前的打开标志。如果数据库没有打开,使用这个方法返回错误。
DB->remove(): 删除指定的数据库。如果database参数没有指定,那么整个数据库文件都会被删除。注意:如果一个数据库有一个打开的句柄,不要删除这个数据库。
DB->rename(): 重命名数据库。
(5) 错误报告函数
为了简化错误报告和处理,DB结构体提供了几个有用的方法。
set_errcall()
set_errfile()
set_errpfx()
err()
errx()
(6) 在enviroments中使用数据库
3. 数据库记录
DB记录包含两个部分,一个key和一些数据。key和其对应的数据data是包含在一个DBT结构中的。因此,访问一个DB的记录,需要两个这样的结构体,一个是key,一个是data。
DBT结构体提供了一个void*域,用于指向你自己的数据data,另一个域是数据长度。因此,它们能被用于保存任何内容,从基本的数据类型到复杂的结构体,只要这些数据是在一个连续的内存块中。
下面介绍DBT的使用,以及如何从数据库保存和获取key/value对。
(1) 使用数据库记录
每一个数据库记录由两个DBT结构体组成,一个是key,另一个是data。
要保存一个数据库的记录,如果其key和data是基本数据类型(int,float等),或者是基本数据类型的数组,我们只需要指定内存的位置和其长度,如下:
DBT key, data;
int iKey=10;
char* iData="this is data";
/* zero out the DBTs before using them. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
key.data=&iKey;
key.size=sizeof(int);
data.data=iData;
data.size=strlen(iData)+1;
要获取一个记录,只需要将DBT中返回的void*给合适的变量即可。
(2) 读写数据库记录
在读写数据库记录的时候,需要注意数据库是否支持重复的记录。如果多个数据库的记录的key相同,它们会被认为是重复的记录。具有同样的key的数据集合称之为重复集(duplicates set)。在DB中,对于重复集,一个给定的key只会被保存一次。
默认情况下,DB数据库不支持重复记录。如果数据库支持重复记录,就需要使用cursor游标来访问重复集中的所有记录了。
DB提供了两种基本的机制来保存和获取数据库的key/data对:
DBT->put()和DBT->get()提供了对于非重复数据的简单的访问;
cursor游标提供了很多方法来保存和获取数据。
下面介绍put和get的使用。
写记录到数据库:
DBT->put()
put函数提供了一个参数是flags,用于控制DB的数据库写的行为。一个比较常用的flag是DB_NOOVERWRITE,这个标志禁用了覆写数据库中已有的记录。如果提供的key在数据库中已经存在,那么put方法就会返回DB_KEYEXIST,即使数据库支持重复记录。
从数据库读记录:
DBT->get()
如果数据库支持重复记录,那么默认情况下这个方法只返回第一个记录。所以,对于支持重复记录的数据库,通常使用cursor来获取记录。(当然,也可以使用整体的get,即使用DB_MULTIPLE标志给get方法,这样它会返回所有的记录,但是要考虑如果记录太多,内存块需要很大)。
默认情况下,get()方法返回的是key匹配的第一条记录,如果数据库支持重复记录,可以通过DB_GET_BOTH标志,会导致get()返回key和data都匹配的第一条记录。
如果指定的key和/或data在数据库中不存在,那么get返回返回DB_NOTFOUND。
删除记录:
DB->del()
用于删除数据库中的记录。如果数据库支持重复记录,那么所有相关的数据都会被删除。如果是从重复记录中删除一条,那么使用cursor游标。
如果要删除数据库中的所有记录,可以使用DB->truncate()。
(3) 在DB中使用结构体
前面说过,DB虽然只有一个key和data,但是data是可以保存任意多个数据的,可以通过结构体来实现保存多个数据。只要结构体的域没有指针,那么可以类似于简单数据类型一样的进行保存和读取。只需要设置其地址为DBT的data,并设置DBT的size即可。
但是,更多的情况,结构体可能需要指针,比如如果是一个很大的字符串,可能需要动态分配内存。对于结构体的欲包含指针的情况,处理起来稍微复杂一点,因为DBT必须要求其data的内容是在连续的内存上的,如果是指针,那么就指向了堆内存,显然不连续,所以,解决方法是通过把结构体的所有域的数据拷贝到一个临时的结构体中,然后进行数据库的读写。
4.