Berkeley DB (DB)介绍

来源:互联网 发布:qq教程网站源码 编辑:程序博客网 时间:2024/04/19 02:27
Berkeley DB (DB)是一个高性能的,嵌入数据库编程库,和C语言,C++,Java,Perl,Python,PHP,Tcl以及其他很多语言都有绑定。BerkeleyDB可以保存任意类型的键/值对,而且可以为一个键保存多个数据。BerkeleyDB可以支持数千的并发线程同时操作数据库,支持最大256TB的数据,广泛用于各种操作系统包括大多数Unix类操作系统和Windows操作系统以及实时操作系统。
        2.0版本或以上的Berkeley DB由SleepycatSoftware公司开发,并使用基于自由软件许可协议/私有许可协议的双重授权方式提供[1],附有源代码。开发者如果想把BerkeleyDB嵌入在私有软件内需要得到Sleepycat公司的许可,若将软件同样遵循GPL发布,则不需许可即可使用。而2.0版本以下的则使用BSD授权,可自由作商业用途。
BerkeleyDB最初开发的目的是以新的HASH访问算法来代替旧的hsearch函数和大量的dbm实现(如AT&T的dbm,Berkeley的ndbm,GNU项目的gdbm),Berkeley DB的第一个发行版在1991年出现,当时还包含了B+树数据访问算法。在1992年,BSDUNIX第4.4发行版中包含了Berkeley DB1.85版。基本上认为这是BerkeleyDB的第一个正式版。在1996年中期,Sleepycat软件公司成立,提供对Berkeley DB的商业支持。在这以后,BerkeleyDB得到了广泛的应用,当前最新版本是4.3.27。
       值得注意的是DB是嵌入式数据库系统,而不是常见的关系/对象型数据库,对SQL语言不支持,也不提供数据库常见的高级功能,如存储过程,触发器等。

Berkeley DB的体系结构

        Berkeley DB以拥有比Microsoft SQLServer和Oracle等数据库系统而言更简单的体系结构而著称。例如,它不支持网络访问—程序通过进程内的API访问数据库。他不支持SQL或者其他的数据库查询语言,不支持表结构和数据列。 访问数据库的程序自主决定数据如何储存在记录里,BerkeleyDB不对记录里的数据进行任何包装。记录和它的键都可以达到4G字节的长度。
       尽管架构很简单,Berkeley DB却支持很多高级的数据库特性,比如ACID 数据库事务处理,细粒度锁,XA接口,热备份以及同步复制。
      Berkeley DB包含有与某些经典Unix数据库编程库兼容的接口,包括:dbm,ndbm和hsearch。

Berkeley DB的核心数据结构

       数据库句柄结构DB:包含了若干描述数据库属性的参数,如数据库访问方法类型、逻辑页面大小、数据库名称等;同时,DB结构中包含了大量的数据库处理函数指针,大多数形式为 (*dosomething)(DB *, arg1, arg2,…)。其中最重要的有open,close,put,get等函数。
       数据库记录结构DBT:DB中的记录由关键字和数据构成,关键字和数据都用结构DBT表示。实际上完全可以把关键字看成特殊的数据。结构中最重要的两个字段是 void * data和u_int32_t size,分别对应数据本身和数据的长度。
数据库游标结构DBC:游标(cursor)是数据库应用中常见概念,其本质上就是一个关于特定记录的遍历器。注意到DB支持多重记录(duplicate records),即多条记录有相同关键字,在对多重记录的处理中,使用游标是最容易的方式。
数据库环境句柄结构DB_ENV:环境在DB中属于高级特性,本质上看,环境是多个数据库的包装器。当一个或多个数据库在环境中打开后,环境可以为这些数据库提供多种子系统服务,例如多线/进程处理支持、事务处理支持、高性能支持、日志恢复支持等。
         DB中核心数据结构在使用前都要初始化,随后可以调用结构中的函数(指针)完成各种操作,最后必须关闭数据结构。从设计思想的层面上看,这种设计方法是利用面向过程语言实现面对对象编程的一个典范。

Berkeley DB数据访问算法

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

B+树算法

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

HASH算法

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

        要求每一个记录都有一个逻辑纪录号,逻辑纪录号由算法本身生成。实际上,这和关系型数据库中逻辑主键通常定义为intAUTO型是同一个概念。Recho建立在B+树算法之上,提供了一个存储有序数据的接口。记录的长度可以为定长或不定长。 和Recno方式接近,只不过记录的长度为定长。数据以定长记录方式存储在队列中,插入操作把记录插入到队列的尾部,相比之下插入速度是最快的。
      对算法的选择首先要看关键字的类型,如果为复杂类型,则只能选择B+树或HASH算法,如果关键字为逻辑记录号,则应该选择Recno或Queue算法。当工作集关键字有序时,B+树算法比较合适;如果工作集比较大且基本上关键字为随机分布时,选择HASH算法。Queue算法只能存储定长的记录,在高的并发处理情况下,Queue算法效率较高;如果是其它情况,则选择Recno算法,Recno算法把数据存储为平面文件格式。

       从DB的官方站点http://www.sleepycat.com/下载最新的软件包db-4.3.27.tar.gz,解压到工作目录,进入该目录,依次执行下列三条命令即可。
../dist/configure
make
make install

执行make uninstall,则可卸载已安装的DB软件。

DB缺省把库和头文件安装在目录/usr/local/BerkeleyDB.4.3/下,使用gcc test.c -ggdb-I/usr/local/BerkeleyDB.4.3/include/ -L/usr/local/BerkeleyDB.4.3/lib/-ldb -lpthread就可正确编译程序。如果读者的测试主机操作系统为REDHAT9,则安装的DB版本可能是4.0。特别要注意到这两个版本的库是不兼容的。例如打开数据库函数DB->open(),在4.0版本中入参为6个,而在4.3版中则为7个(可自行比较两个库的头文件db.h中DB->open函数的定义)。因为在DB相关的应用程序中,open函数基本上都是要执行的,所以如果函数和版本不匹配,编译肯定会出错。当然,编译完成后,可以使用命令ldd查看库的依赖关系。


DB常用函数使用范例

#include <db.h> 
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

/* DB的函数执行完成后,返回0代表成功,否则失败 */
void print_error(int ret)
{
if(ret != 0)
printf("ERROR: %s/n",db_strerror(ret));
}

/* 数据结构DBT在使用前,应首先初始化,否则编译可通过但运行时报参数错误 */
void init_DBT(DBT * key, DBT * data)
{
memset(key, 0, sizeof(DBT));
memset(data, 0, sizeof(DBT));
}

void main(void)
{
DB *dbp;
DBT key, data;
u_int32_t flags;
int ret;

char *fruit = "apple";
int number = 15;

typedef struct customer
{
int c_id;
char name[10];
char address[20];
int age;
} CUSTOMER;
CUSTOMER cust;
int key_cust_c_id = 1;

cust.c_id = 1;
strncpy(cust.name, "javer", 9);
strncpy(cust.address, "chengdu", 19);
cust.age = 32;

/* 首先创建数据库句柄 */
ret = db_create(&dbp, NULL, 0);
print_error(ret);

/* 创建数据库标志 */
flags = DB_CREATE;

/* 创建一个名为single.db的数据库,使用B+树访问算法,本段代码演示对简单数据类型的处理 */
ret = dbp->open(dbp, NULL, "single.db", NULL, DB_BTREE, flags, 0);
print_error(ret);

init_DBT(&key, &data);

/* 分别对关键字和数据赋值和规定长度 */
key.data = fruit;
key.size = strlen(fruit) + 1;
data.data = &number;
data.size = sizeof(int);

/* 把记录写入数据库中,不允许覆盖关键字相同的记录 */
ret = dbp->put(dbp, NULL, &key, &data,DB_NOOVERWRITE);
print_error(ret);

/* 手动把缓存中的数据刷新到硬盘文件中,实际上在关闭数据库时,数据会被自动刷新 */
dbp->sync();

init_DBT(&key, &data);

key.data = fruit;
key.size = strlen(fruit) + 1;

/* 从数据库中查询关键字为apple的记录 */
ret = dbp->get(dbp, NULL, &key, &data, 0);
print_error(ret);

/* 特别要注意数据结构DBT的字段data为void *型,所以在对data赋值和取值时,要做必要的类型转换。 */
printf("The number = %d/n", *(int*)(data.data));

if(dbp != NULL)
dbp->close(dbp, 0);

ret = db_create(&dbp, NULL, 0);
print_error(ret);

flags = DB_CREATE;

/* 创建一个名为complex.db的数据库,使用HASH访问算法,本段代码演示对复杂数据结构的处理 */
ret = dbp->open(dbp, NULL, "complex.db", NULL, DB_HASH, flags, 0);
print_error(ret);

init_DBT(&key, &data);

key.size = sizeof(int);
key.data = &(cust.c_id);

data.size = sizeof(CUSTOMER);
data.data = &cust;

ret = dbp->put(dbp, NULL, &key, &data,DB_NOOVERWRITE);
print_error(ret);

memset(&cust, 0, sizeof(CUSTOMER));

key.size = sizeof(int);
key.data = &key_cust_c_id;

data.data = &cust;
data.ulen = sizeof(CUSTOMER);
data.flags = DB_DBT_USERMEM;

dbp->get(dbp, NULL, &key, &data, 0);
print_error(ret);

printf("c_id = %d name = %s address = %s age = %d/n",
cust.c_id, cust.name, cust.address, cust.age);

if(dbp != NULL)
dbp->close(dbp, 0);
}

[编辑]

DB游标使用范例

游标是依赖于数据库句柄的,应用程序代码框架如下:

/* 定义一个游标变量 */
DBC * cur;
/* 首先打开数据库,再打开游标 */
dbp->open(dbp, ……);
dbp->cursor(dbp, NULL, &cur, 0);

/* do something with cursor */

/* 首先关闭,在关闭数据库 */
cur->c_close(cur);
dbp->close(dbp, 0);

在游标打开后,可以以多种方式遍历特定记录。

Memset(&key, 0, sizeof(DBT));
Memset(&data, 0, sizeof(DBT));

/* 因为KEY和DATA为空,则游标遍历整个数据库记录 */
While((ret = cur->c_get(cur, &key, &data, DB_NEXT)) == 0)
{
/* do something with key and data */
}

当想查询特定关键字对应的记录,则应对关键字赋值,并把cur->c_get()函数中标志位设置为DB_SET。例如:

key.data = "xxxxx";
key.size = XXX;
While((ret = cur->c_get(cur, &key, &data, DB_SET)) == 0)
{
/* do something with key and data */
}

游标的作用还有很多,如查询多重记录,插入/修改/删除记录等。

[编辑]

DB环境使用范例

环境是DB数据库的包装器,提供多种高级功能。应用程序代码框架如下:

/* 定义一个环境变量,并创建 */
DB_ENV *dbenv;
db_env_create(&dbenv, 0);

/* 在环境打开之前,可调用形式为dbenv->set_XXX()的若干函数设置环境 */
/* 通知DB使用Rijndael加密算法(参考资料4)对数据进行处理 */
dbenv->set_encrypt(dbenv, "encrypt_string", DB_ENCRYPT_AES);
/* 设置DB的缓存为5M */
dbenv->set_cachesize(dbenv, 0, 5 * 1024 * 1024, 0);
/* 设置DB查找数据库文件的目录 */
dbenv->set_data_dir(dbenv, "/usr/javer/work_db");

/* 打开数据库环境,注意后四个标志分别指示DB启动日志、加锁、缓存、事务处理子系统 */
dbenv->open(dbenv,home,DB_CREATE|DB_INIT_LOG|DB_INIT_LOCK| DB_INIT_MPOOL|DB_INIT_TXN, 0);

/* 在环境打开后,则可以打开若干个数据库,所有数据库的处理都在环境的控制和保护中。注意db_create函数的第二个参数是环境变量 */

db_create(&dbp1, dbenv, 0);
dbp1->open(dbp1, ……);

db_create(&dbp2, dbenv, 0);
dbp1->open(dbp2, ……);

/* do something with the database */

/* 最后首先关闭打开的数据库,再关闭环境 */
dbp2->close(dbp2, 0);
dbp1->close(dbp1, 0);
dbenv->close(dbenv, 0);


参考资料:
 1.http://wiki.ccw.com.cn/Berkeley_DB
原创粉丝点击