Berkeley DB是一个开源的文件数据库,介于关系数据库与
内存数据库之间,使用方式与内存数据库类似,它提供的是一系列直接访问数据库的函数,而不是像关系数据库那样需要网络通讯、SQL解析等步骤。
简介
Berkeley DB是历史悠久的
嵌入式数据库系统,主要应用在UNIX/LINUX操作系统上,其设计思想是简单、小巧、可靠、高性能。
Berkeley DB (DB)是一个高性能的,嵌入数据库编程库,和C语言,C++,Java,Perl,Python,PHP,Tcl以及其他很多语言都有绑定。Berkeley DB可以保存任意类型的键/值对,而且可以为一个键保存多个数据。Berkeley DB可以支持数千的并发线程同时操作数据库,支持最大256TB的数据,广泛
用于各种操作系统包括大多数Unix类操作系统和Windows操作系统以及
实时操作系统。
2.0版本或以上的Berkeley DB由Sleepycat Software公司开发,并使用基于自由软件许可协议/私有许可协议的双重授权方式提供[1],附有源代码。开发者如果想把Berkeley DB嵌入在
私有软件内需要得到Sleepycat公司的许可,若将软件同样遵循GPL发布,则不需许可即可使用。而2.0版本以下的则使用BSD授权,可自由作商业用途。
Berkeley DB最初开发的目的是以新的HASH访问算法来代替旧的hsearch函数和大量的dbm实现(如AT&T的dbm,Berkeley的 ndbm,GNU项目的gdbm),Berkeley DB的第一个发行版在1991年出现,当时还包含了B+树数据访问算法。在这以后,Berkeley DB得到了广泛的应用,成为一款独树一帜的
嵌入式数据库系统。2006年Sleepycat公司被Oracle 公司收购,Berkeley DB成为
Oracle数据库家族的一员,Sleepycat原有开发者继续在Oracle开发Berkeley DB,Oracle继续原来的授权方式并且加大了对Berkeley DB的开发力度,继续提升了Berkeley DB在软件行业的声誉。Berkeley DB的当前最新发行版本是6.4.9。
值得注意的是DB是
嵌入式数据库系统,而不是常见的关系/对象型数据库,对SQL语言不支持(目前已经支持SQL),也不提供数据库常见的高级功能,如
存储过程,
触发器等。
结构
Berkeley DB以拥有比Microsoft SQL Server和Oracle等
数据库系统而言更简单的体系结构而著称。例如,它不支持网络访问—程序通过进程内的API访问数据库。 他不支持其他的数据库查询语言,最新的版本已支持sql,不支持表结构和数据列。 访问数据库的程序自主决定数据如何储存在记录里,Berkeley DB不对记录里的数据进行任何包装,每个记录有且只有两部分:键、值,所以在Berkeley DB的背景下通常用key/data pair指代一个记录。记录和它的键都可以达到4G字节的长度。
尽管架构很简单,Berkeley DB却支持很多高级的数据库特性,比如ACID 数据库事务处理,细粒度锁,XA接口,热备份以及同步复制。
DB中核心数据结构在使用前都要初始化,随后可以调用结构中的函数(
指针)完成各种操作,最后必须关闭数据结构。从设计思想的层面上看,这种设计方法是利用
面向过程语言实现面对对象编程的一个典范。
Berkeley DB数据访问算法
在数据库领域中,数据访问算法对应了数据在硬盘上的存储格式和操作方法。在编写应用程序时,选择合适的算法可能会在
运算速度上提高1个甚至多个数量级。大多数数据库都选用B+树算法,DB也不例外,同时还支持HASH算法、Recno算法和Queue算法。接下来,我们将讨论这些算法的特点以及如何根据需要存储数据的特点进行选择。
B+树算法
B+树是一个
平衡树,关键字有序存储,并且其结构能随数据的插入和删除进行动态调整。为了代码的简单,DB没有实现对
关键字的前缀码压缩。B+树支持对数据查询、插入、删除的常数级速度。关键字可以为任意的数据结构.
HASH算法
DB中实际使用的是扩展线性HASH算法(extended linear hashing),可以根据HASH表的增长进行适当的调整。关键字可以为任意的数据结构。
要求每一个记录都有一个逻辑纪录号,逻辑纪录号由算法本身生成。实际上,这和
关系型数据库中逻辑主键通常定义为int AUTO型是同一个概念。Recho建立在B+树算法之上,提供了一个存储有序数据的接口。记录的长度可以为定长或不定长。 和Recno方式接近, 只不过记录的长度为定长。数据以定长记录方式存储在队列中,插入操作把记录插入到队列的尾部,相比之下插入速度是最快的。
对算法的选择首先要看关键字的类型,如果为复杂类型,则只能选择B+树或HASH算法,如果关键字为逻辑
记录号,则应该选择Recno或Queue算法。当
工作集关键字有序时,B+树算法比较合适;如果工作集比较大且基本上关键字为随机分布时,选择HASH算法。Queue算法只能存储定长的记录,在高的并发处理情况下,Queue
算法效率较高;如果是其它情况,则选择Recno算法,Recno算法把
数据存储为平面文件格式。
Berkeley DB包含有与某些经典Unix数据库编程库兼容的接口,包括:dbm,ndbm和hsearch。
Berkeley DB的核心数据结构
数据库环境句柄DB_ENV:每个DB_ENV相当于一个数据库,它包含了数据库全局信息,比如
缓冲区大小、以及对
事务、
日志、锁等子系统的全局配置信息。
数据库句柄结构DB:每个DB相当于关系数据库的一个表,其中存储了很多key/data pair。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中属于高级特性,本质上看,环境是多个数据库的包装器。当一个或多个数据库在环境中打开后,环境可以为这些数据库提供多种子系统服务,例如多线/进程处理支持、
事务处理支持、高性能支持、日志恢复支持等。
发展历史
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,根据不同数据类型,可以选择适当的算法以达到最佳性能。
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。
系统特点
1. 访问速度快
2. 省硬盘空间
Berkeley DB可以轻松支持上千个线程同时访问数据库,支持多进程、事务等特性。
Berkeley DB运行在大多数的操作系统中,例如大多数的UNIX系统, 和windows系统,以及实时操作系统。
Berkeley DB 还拥有对一些老的UNIX数据库,例如dbm, ndbm und hsearch的兼容接口.
对于在java系统中的使用,Berkeley DB提供了一个压缩成jar单个文件的java版本。 这个版本可以运行在
java虚拟机上使用,并且拥有和C语言版本相同的所有操作和功能。
Berkeley DB XML,是一个接口,通过它可以实现对XML数据存贮的支持。对XML数据的访问,会使用相应的查询语句如Xquery, Xpath。
Berkeley DB只支持单一的数据结构,它的所有数据包括两个部分:key 和 data.
Berkeley DB原则上是为
嵌入式数据库设计的。
范例
函数使用
#include
#include
#include
/* DB的函数执行完成后,返回0代表成功,否则失败 */
void print_error(int ret)
{
if(ret != 0)
}
/* 数据结构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;
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;
cust.age = 32;
/* 首先创建数据库句柄 */
ret = db_create(&dbp, NULL, 0);
print_error(ret);
/* 创建数据库标志 */
flags = DB_CREATE;
/* 创建一个名为single.db的数据库,使用B+树访问算法,本段代码演示对简单数据类型的处理 */
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赋值和取值时,要做必要的类型转换。 */
if(dbp != NULL)
dbp->close(dbp, 0);
ret = db_create(&dbp, NULL, 0);
print_error(ret);
flags = DB_CREATE;
/* 创建一个名为complex.db的数据库,使用HASH访问算法,本段代码演示对复杂数据结构的处理 */
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);
cust.c_id, cust .name, cust.address, cust.age);
if(dbp != NULL)
dbp->close(dbp, 0);
}
游标使用
游标是依赖于数据库句柄的,应用程序代码框架如下:
/* 定义一个游标变量 */
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.size = XXX;
While((ret = cur->c_get(cur, &key, &data, DB_SET)) == 0)
{
/* do something with key and data */
}
游标的作用还有很多,如查询多重记录,插入/修改/删除记录等。
环境使用
环境是DB数据库的包装器,提供多种高级功能。应用程序代码框架如下:
/* 定义一个环境变量,并创建 */
DB_ENV *dbenv;
db_env_create(&dbenv, 0);
/* 在环境打开之前,可调用形式为dbenv->set_XXX()的若干函数设置环境 */
/* 通知DB使用
Rijndael加密算法(参考资料4)对数据进行处理 */
/* 设置DB的缓存为5M */
dbenv->set_cachesize(dbenv, 0, 5 * 1024 * 1024, 0);
/* 设置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);