[转载] Berkeley DB简介

    Berkeley DB是一个开源的文件数据库,介于关系数据库与内存数据库之间,使用方式与内存数据库类似,它提供的是一系列直接访问数据库的函数,而不是像关系数据库那样需要网络通讯、SQL解析等步骤。 
    Berkeley DB函数库早期版本只有300K大小,但却可管理高达256TB的数据,现在的最新版4.7也只有几MB,既能在小型的嵌入式设备上使用,也可以在大型设备上管理重要的数据。它目前已经应用在Apache、MySQL、Sendmail、Subversion、OpenLDAP、Mozilla、Google等很多地方,而在MySQL数据库中担任的更是核心数据处理引擎,使MySQL成为一个小型的快速的关系数据库,不过从MySQL5.1版本开始不再使用Berkeley DB,因为它已经被Oracle以10亿美金之巨收归囊中,而MySQL也已进了Sun的家门。
    Berkeley DB对C、C++、Perl、Java、Python、Ruby、PHP等基本上所有的语言都提供了接口,对一条记录只分为两个字段,一个为键,一个为值,键与值可以是任意的数据,并且可以长达4GB,它提供了四种数据存取算法:B+树、Hash、Recno、Queue,根据不同数据类型,可以选择适当的算法以达到最佳性能。
   Berkeley DB可以轻松支持上千个线程同时访问数据库,支持多进程、事务等特性。
   Berkeley DB历史:
     1991年,Berkeley DB的第一个版发行(Linux系统也是在这一年诞生)。
     1992年,BSD UNIX第4.4发行版中包含了Berkeley DB1.85版。基本上认为这是Berkeley DB的第一个正式版。
     1996年,Sleepycat软件公司成立,提供对Berkeley DB的商业支持。
     2006年,Sleepycat被Oracle收购,当前最新版本是4.7.25。
     2009年,SUN被Oracle收购,不知道MySQL会不会再次启用Berkeley DB。

做一个项目时候,为了作预研,写了这么一段,今天翻翻历史,无意中看到了,觉得还有点价值,粘上来占个座.

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(),默认用字典顺序比较
默认不支持重复记录,相同的记录采用覆盖掉现有的记录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值