Berkeley DB数据库

整理转载自:https://www.ibm.com/developerworks/cn/linux/l-embdb/

Berkeley DB是历史悠久的嵌入式数据库系统,主要应用在UNIX/LINUX操作系统上,其设计思想是简单、小巧、可靠、高性能。

DB的设计思想

DB的设计思想是简单、小巧、可靠、高性能。如果说一些主流数据库系统是大而全的话,那么DB就可称为小而精。DB提供了一系列应用程序接口(API),调用本身很简单,应用程序和DB所提供的库在一起编译成为可执行程序。这种方式从两方面极大提高了DB的效率。第一:DB库和应用程序运行在同一个地址空间,没有客户端程序和数据库服务器之间昂贵的网络通讯开销,也没有本地主机进程之间的通讯;第二:不需要对SQL代码解码,对数据的访问直截了当。

DB对需要管理的数据看法很简单,DB数据库包含若干条记录,每一个记录由关键字和数据(KEY/VALUE)构成。数据可以是简单的数据类型,也可以是复杂的数据类型,例如C语言中结构。DB对数据类型不做任何解释, 完全由程序员自行处理,典型的C语言指针的”自由”风格。如果把记录看成一个有n个字段的表,那么第1个字段为表的主键,第2–n个字段对应了其它数据。DB应用程序通常使用多个DB数据库,从某种意义上看,也就是关系数据库中的多个表。DB库非常紧凑,不超过500K,但可以管理大至256T的数据量。

DB的设计充分体现了UNIX的基于工具的哲学,即若干简单工具的组合可以实现强大的功能。DB的每一个基础功能模块都被设计为独立的,也即意味着其使用领域并不局限于DB本身。例如加锁子系统可以用于非DB应用程序的通用操作,内存共享缓冲池子系统可以用于在内存中基于页面的文件缓冲。

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中核心数据结构在使用前都要初始化,随后可以调用结构中的函数(指针)完成各种操作,最后必须关闭数据结构。从设计思想的层面上看,这种设计方法是利用面向过程语言实现面对对象编程的一个典范。

DB数据访问算法

在数据库领域中,数据访问算法对应了数据在硬盘上的存储格式和操作方法。在编写应用程序时,选择合适的算法可能会在运算速度上提高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软件的安装和编译

从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就可正确编译程序。如果读者的测试主机操作系统为RED HAT9,则安装的DB版本可能是4.0。特别要注意到这两个版本的库是不兼容的。例如打开数据库函数DB->open(),在4.0版本中入参为6个,而在4.3版中则为7个(可自行比较两个库的头文件db.h中DB->open函数的定义)。因为在DB相关的应用程序中,open函数基本上都是要执行的,所以如果函数和版本不匹配,编译肯定会出错。当然,编译完成后,可以使用命令ldd查看库的依赖关系。

DB的常用操作

创建/打开数据文件

首先调用db_create()函数来创建DB结构的一个实例,然后再调用DB->open()函数来完成真正的打开操作。Berkeley DB 将所有对数据库的操作都封装在名为DB 的结构中。db_create()函数的作用就是创建一个该结构。将磁盘上保存的文件作为数据库打开是由DB->open()函数来完成的,其原型如下所示:

int DB->open(DB *db, DB_TXN *txnid, const char *file, 
                    const char *database, DBTYPE type, u_int32_t flags, int mode);
eg:
if ((ret = db_create(&dbp, NULL, 0)) != 0) { 
     fprintf(stderr, "db_create: %s\n", 
    db_strerror(ret)); 
    exit (1); 
 } 

 if ((ret = dbp->open(dbp, NULL, DATABASE, NULL, 
     DB_BTREE, DB_CREATE, 0664)) != 0) { 
    dbp->err(dbp, ret, "%s", DATABASE); 
    exit (1); 
 } 

参数“DATABASE”指明对应的磁盘文件名为demo.db;参数“DB_BTREE”表示数据库底层使用的数据结构是B 树;而参数“DB_CREATE”和“0664”则表明当数据库文件不存在时创建一个新的数据库文件,并且将该文件的属性值设置为0664。

错误处理是在打开数据库时必须的例行检查,这可以通过调用DB->err()函数来完成。其中 参数“ret”是在调用Berkeley DB 函数后返回的错误代码,其余参数则用于显示结构化的错误信息。

插入数据包

向Berkeley DB 数据库中添加数据可以通过调用DB->put()函数来完成,其原型如下所示:

int DB->put(DB *db, DB_TXN *txnid, DBT *key, DBT *data,u_int32_t flags);

在向数据库中添加数据时,如果给定的关键字已经存在,大多数应用会对于已经存在的数据采用覆盖原则。也就是说,如果数据库中已经保存了一个“sport/basketball”对,再次调用DB->put()函数添加一个“sport/football”对,那么先前保存的那些数据将会被覆盖。但Berkeley DB 允许在调用DB->put()函数时指定参数“DB_NOOVERWRITE”,声明不对数据库中已经存在的数据进行覆盖,一旦给出“DB_NOOVERWRITE”标记,如果DB->put()函数在执行过程中发现给出的关键字在数据库中已经存在了,就无法成功地把该Key/Data 对添加到数据库中,于是将返回错误代码 “DB_KEYEXIST”。

检索数据包

从Berkeley DB 数据库中检索数据可以通过调用DB->get()函数来完成,其原型如下所示:

int DB->get(DB *db, DB_TXN *txnid, DBT *key, DBT *data,u_int32_t flags);

在进行数据检索时,对DB->get()函数的返回值进行处理是必不可少的,因为它携带着检索操作是否成功完成等信息。下面列出的是DB->get()函数的返回值:

  • 0 函数调用成功,指定的关键字被找到;

  • DB_NOTFOUND 函数调用成功,但指定的关键字未被找到;

  • 大于0 函数调用失败,可能出现了系统错误。

删除数据包

从Berkeley DB 数据库中删除数据可以通过调用DB->del()函数来完成,其原型如下所示:

int DB->del(DB *db, DB_TXN *txnid, DBT *key, u_int32_t flags);

删除数据只需给出相应的关键字,不用指明与之对应的数据。

key.data = "sport"; 
key.size = sizeof("sport"); 
if ((ret = dbp->del(dbp, NULL, &key, 0)) == 0) 
printf("db: %s: key was deleted.\n", (char*)key.data); 
else
dbp->err(dbp, ret, "DB->del");

关闭数据文件

对于一次完整的数据库操作过程来说,关闭数据库是不可或缺的一个环节。这是因为Berkeley DB 需要依赖于系统底层的缓冲机制,也就是说只有在数据库正常关闭的时候,修改后的数据才有可能全部写到磁盘上,同时它所占用的资源也才能真正被全部释放。关闭数据库的操作是通过调用DB->close()函数来完成的,其原型如下所示:

int DB->close(DB *db, u_int32_t flags);

刷新缓存到硬盘

Berkeley DB的所有操作都是在内存中进行,在关闭数据库句柄时,会将所有数据写入到硬盘中;在程序运行中,如需要同步数据到硬盘,可调用sync()函数。

注:这个函数在4.3之前的版本是无参的,而在之后的版本增加参数,修改之前的例子需注意兼容;

dbp->sync(dbp,0);

删除数据库

DB->remove()函数用于删除指定的数据文件,如果在参数中没有指定数据包名称,则整个数据文件将被删除

#include<db.h>
...
DB *dbp;
...
dbp->remove(
    dbp,         //数据包标识符指针
    "mydb.db",   //数据文件名称
    NULL,        //数据包名称,这里没有指定,则删除整个文件
    0);          //操作标志
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值