BerkeleyDB资料收集

来源:互联网 发布:java简易局域网聊天 编辑:程序博客网 时间:2024/06/07 09:22

转自http://bbs.chinaunix.net/thread-1490864-1-1.html
Berkeley DB

300K大小的数据库,可管理高达256TB的数据(最早期版,现在的4.7版为1MB左右)

应用实例:Apache、MySQL、Sendmail、Subversion、OpenLDAP、Mozilla、Google

数据访问算法:

在数据库领域中,数据访问算法对应了数据在硬盘上的存储格式和操作方法。在编写应用程序时,选择合适的算法可能会在运算速度上提高1个甚至多个数量级。大多数数据库都选用B+树算法,DB也不例外,同时还支持HASH算法、Recno算法和Queue算法。接下来,我们将讨论这些算法的特点以及如何根据需要存储数据的特点进行选择。 

B+树算法:B+树是一个平衡树,关键字有序存储,并且其结构能随数据的插入和删除进行动态调整。为了代码的简单,DB没有实现对关键字的前缀码压缩。B+树支持对数据查询、插入、删除的常数级速度。关键字可以为任意的数据结构。

HASH算法:DB中实际使用的是扩展线性HASH算法(extended linear hashing),可以根据HASH表的增长进行适当的调整。关键字可以为任意的数据结构。 

Recno算法: 要求每一个记录都有一个逻辑纪录号,逻辑纪录号由算法本身生成。实际上,这和关系型数据库中逻辑主键通常定义为int AUTO型是同一个概念。Recho建立在B+树算法之上,提供了一个存储有序数据的接口。记录的长度可以为定长或不定长。 

Queue算法:和Recno方式接近, 只不过记录的长度为定长。数据以定长记录方式存储在队列中,插入操作把记录插入到队列的尾部,相比之下插入速度是最快的。 

对算法的选择首先要看关键字的类型,如果为复杂类型,则只能选择B+树或HASH算法,如果关键字为逻辑记录号,则应该选择Recno或Queue算法。当工作集关键字有序时,B+树算法比较合适;如果工作集比较大且基本上关键字为随机分布时,选择HASH算法。Queue算法只能存储定长的记录,在高的并发处理情况下,Queue算法效率较高;如果是其它情况,则选择Recno算法,Recno算法把数据存储为平面文件格式。

创建句柄:
DB *dbp;                        /* DB structure handle */
int ret;                        /* function return value */

/* db_create()相当于C++接口中的构造函数 */
ret = db_create(&dbp, NULL, 0);        /* Initialize the structure. This
                                 * database is not opened in an environment,
                                 * so the environment pointer is NULL. */

打开数据库:
ret = dbp->open(dbp,                /* DB structure pointer */
                NULL,                /* Transaction pointer */
                "test.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) */
常用的flags有DB_CREATE、DB_EXCL、DB_RDONLY、DB_TRUNCATE

删除数据库:
dbp->remove(dbp,                /* Database pointer */
                "test.db",        /* Database file to remove */
                NULL,                /* Database to remove. This is
                                 * NULL so the entire file is
                                 * removed. */
                0);                /* Flags. None used. */


更名数据库:
dbp->rename(dbp,                /* Database pointer */
                "test.db",        /* Database file to rename */
                NULL,                /* Database to rename. This is
                                 * NULL so the entire file is
                                 * renamed. */
                "newname.db",        /* New database file name */
                0);                /* Flags. None used. */

注:删除、更名数据库后不能打开,否则会段错误

错误处理:
printf("%s\n",db_strerror(ret));/* db_strerror() */

打开环境:
DB_ENV *myEnv;                        /* Env structure handle */
DB *dbp;                        /* DB structure handle */
u_int32_t db_flags;                /* database open flags */
u_int32_t env_flags;                /* env open flags */
int ret;                        /* function return value */
ret = db_env_create(&myEnv, 0);        /* Create an environment object */
env_flags = DB_CREATE |                /* If the environment does not exist,
                                   create it. */
            DB_INIT_MPOOL;        /* Initialize the in-memory cache. */
ret = myEnv->open(myEnv,        /* DB_ENV ptr */
                "/export1/testEnv",        /* env home directory */
                env_flags,                /* Open flags */
                0);                        /* File mode (default) */
ret = db_create(&dbp, myEnv, 0);

增加记录:
DBT key, data;
float money = 122.45;
char *description = "Grocery bill.";
/* Zero out the DBTs before using them. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
key.data = &money;
key.size = sizeof(float);
data.data = description;
data.size = strlen(description) + 1;

ret = my_database->put(my_database, NULL, &key, &data, DB_NOOVERWRITE);        /* 如果不指定DB_NOOVERWRITE,将覆盖记录 */
if (ret == DB_KEYEXIST) {
        printf("Put failed because key %f already exists", money);
}


获取记录:
float money;
DBT key, data;
char *description;
/* Initialize the DBTs */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
key.data = &money;
key.ulen = sizeof(float);
key.flags = DB_DBT_USERMEM;                /* 只有在指定了DB_DBT_USERMEM时,ulen才有效,否则,data将指向BerkeleyDB分配的缓冲区,ulen为用户的缓冲区大小,要大于等于实际记录的长度 */
/* Database retrieval code goes here */
/*
* Money is set into the memory that we supplied.
*/
description = data.data;
my_database->get(my_database, NULL, &key, &data, 0);

删除记录:
my_database->del(my_database, NULL, &key, 0);        /* 如果支持重复记录,将删掉key的所有记录,此时,可使用游标删除单条记录 */

清空数据库:
DB->truncate()
dbp->truncate(dbp,NULL,&count,0);        /* 可能只是将记录数改为0,因为文件大小没变,数据通过VI也可以看出来 */

数据恢复:
DB->verify();                /* 检查数据库是否完整 */
如果没有使用事务,在程序意外崩溃或机器崩溃时,可以用db_dump工具恢复数据。

打开游标:
DB *my_database;
DBC *cursorp;
my_database->cursor(my_database, NULL, &cursorp, 0);

关闭游标:
if (cursorp != NULL)
        cursorp->c_close(cursorp);

搜索记录:
cursorp->c_get();
/* Initialize our DBTs. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
/* Iterate over the database, retrieving each record in turn. */
while ((ret = cursorp->c_get(cursorp, &key, &data, DB_NEXT)) == 0) {
        /* Do interesting things with the DBTs here. */
}
if (ret != DB_NOTFOUND) {
        /* Error handling goes here */
}
c_get()的执行标志:
DB_NEXT:从头到尾,遍历数据库
DB_PREV:从尾到头,遍历数据库
DB_SET:指向等于key的记录集
DB_SET_RANGE:指向大于等于key的记录集(如按字母排序)
DB_GET_BOTH:指向既等于key,又等于data的记录
DB_GET_BOTH_RANGE:指向等于key,大于等于data的记录集
DB_NEXT_NODUP:遍历但跳过相同key的记录
DB_PREV_NODUP:遍历但跳过相同key的记录

插入记录:
cursorp->c_put();
c_put()的执行标志:
DB_NODUPDATA:如果相同key的记录已存在,则返回DB_KEYEXIST错误。该标志只有在数据库支持重复记录时才有效(即创建数据库时,指定DB_DUPSORT标志)
DB_KEYFIRST:如果数据库不支持重复记录,则覆盖相同key的记录,如果支持重复记录,则插入到相同key的记录集开头。
DB_KEYLAST:与DB_KEYFIRST作用相反

删除记录:
cursorp->c_del();
/* Iterate over the database, deleting each record in turn. */
while ((ret = cursorp->c_get(cursorp, &key,
        &data, DB_SET)) == 0) {
        cursorp->c_del(cursorp, 0);
}

更新记录:
cursorp->c_put();
cursorp->c_put(cursorp, &key, &data, DB_CURRENT);        /* 用DB_CURRENT标志,这个做法对重复记录可能存在一些问题,因此可以使用先删除,后增加的方式来更新记录。*/

副数据库:
/* Now associate the secondary to the primary */
dbp->associate(dbp,                /* Primary database */
                NULL,                /* TXN id */
                sdbp,                /* Secondary database */
                get_sales_rep,        /* Callback used for key creation. Not
                                 * defined in this example. See the next
                                 * section. */
                0);                /* Flags */
回调函数:
int
get_sales_rep(DB *sdbp,                        /* secondary db handle */
                const DBT *pkey,        /* primary db record's key */
                const DBT *pdata,        /* primary db record's data */
                DBT *skey)                /* secondary db record's key */
{
        return 0;
}
副数据库只能手工查找、删除记录,不能修改或增加记录,若需要修改记录,直接修改主数据库中的记录,然后通过回调函数自动完成对副数据库的修改。

数据库管理:
获取数据库统计信息:DB->stat()
设置页大小:DB->set_pagesize(),BTree型数据库的页大小最好至少能够容纳4笔记录,因为跨页取记录开销很大。
锁:多线程或多进程访问时,会使用到锁,而数据库一般提供的是页级锁(Queue型数据库除外),而不是记录锁。一个页容纳的记录数越多,访问同一页的概率就越大,锁的概率越大,最终性能就越低,因此页的大小要综合考虑。
锁的统计信息:DB_ENV->lock_stat()
IO效率:磁盘一般以块存取,当块大小与页大小相同时,效率最高。
设置缓冲区大小:DB->set_cachesize()或DB_ENV->set_cachesize()
BTree细节:
设置比较函数:DB->set_bt_compare(),默认用字典顺序比较
默认不支持重复记录,相同的记录采用覆盖掉现有的记录。
原创粉丝点击