Berkeley DB安装及配置

BerkeleyDB简介

Berkeley DB(BDB)是一个高性能的,嵌入数据库编程库,和C语言C++JavaPerlPythonTcl以及其他很多语言都有应用程序编程界面。Berkeley DB可以保存任意类型的键/值对,而且可以为一个键保存多个数据。Berkeley DB可以支持数千的并发线程同时操作数据库,支持最大256TB的数据,广泛用于各种操作系统包括大多数Unix类操作系统和Windows操作系统以及实时操作系统

源起

Berkeley DB最先由伯克利加州大学为了移除受到AT&T限制的程式码,从BSD 4.3到4.4时所改写的软件。在1996年时,网景公司希望Berkeley DB的作者群改善函式库,以配合网景公司所需要的LDAP服务器,以及在Netscape 浏览器,因此促成了Sleepycat Software(在2006年时2月时Sleepycat Software被甲骨文公司所买下)。

Berkeley DB以Sleepycat Public License发行(该授权条款为OSI及FSF所认可的条款)。发行时包括了完整的程式码、编译工具、测试套件,以及说明文件。由于良好的程式码的品质以及工具程式,Berkeley DB常被其他开放源代码软件所使用。对于不想使用Sleepycat Public License的开发团体,甲骨文公司也提供了其他付费的授权方式。

Berkeley DB包含有与某些经典Unix数据库编程库兼容的接口,包括:dbmndbmhsearch

[编辑]特征

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原则上是为嵌入式数据库设计的。


体系结构

Berkeley DB以拥有比Microsoft SQL ServerOracle等数据库系统而言更简单的体系结构而著称。例如,它不支持网络访问—程序通过进程内的API访问数据库。 他不支持SQL或者其他的数据库查询语言,不支持表结构和数据列。 访问数据库的程序自主决定数据如何储存在记录里,Berkeley DB不对记录里的数据进行任何包装。记录和它的键都可以达到4G字节的长度。

尽管架构很简单,Berkeley DB却支持很多高级的数据库特性,比如ACID 数据库事务处理, 细粒度, XA接口,热备份以及同步复制


使用Berkeley DB的程式

Berkeley DB是一些轻量级目录访问协议服务器,数据库系统以及其他很多商业和[开源]应用的底层存储系统。下面是使用了Berkeley DB的知名软件的名单。


MySQL
与Berkeley DB

作为全世界最流行的轻量级数据库之一,MySQL也曾经采用过Berkeley DB作为其底层数据引擎,但因Berkeley DB最终被数据库厂商Oracle收购,并且改变了其版权许可协议性质,所以MySQL不再使用其作为数据引擎之一。


授权方式

2.0版本或以上的Berkeley DB使用双重授权(见http://www.sleepycat.com/download/licensinginfo.shtml),而2.0版本以下的则使用BSD授权,可自由作商业用途。

安装过程:

(环境: Cent OS 5.4)

从其oracle公司官网http://www.oracle.com/technetwork/database/berkeleydb/downloads/index.html获得其安装包:http://download.oracle.com/otn/berkeley-db/db-5.1.19.NC.tar.gz
#tar zxfv db-5.1.19.NC.tar.gz
#cd db-5.1.19.NC/build_unix
(仅用于unix平台安装)
#../dist/configure
(主意:
1、如果以gcc和g++编译的话,须执行#env CC=gcc CCC=g++ ../dist/configure,否则编译时出现段错误
2、默认状态,berkeleyDB的lib和include将被安装到/usr/local/BerkeleyDB/下,需要更改这个路径的话,可以加上选项–prefix,例如:#../dist/configure –prefix=/opt/BerkeleyDB.)
#make
#make install
#vi /etc/ld.so.conf
(并将berkeleyDB的lib路径加到该文件的最后一行,这样才能找到并加载它的库文件.ld.so.conf是系统动态链接库的配置文件.此文件内,存放着可被LINUX共享的动态链接库所在目录的名字(系统目录/lib,/usr/lib除外),各个目录名间以空白字符(空格,换行等)或冒号或逗号分隔.)
#ldconfig
(#make uninstall卸载)

安装过程中有如下提示:
———————————————————————-

Libraries have been installed in:
/usr/local/BerkeleyDB.5.1/lib

If you ever happen to want to link against installed libraries in a given directory, LIBDIR, you must either use libtool, and specify the full pathname of the library, or use the `-LLIBDIR’ flag during linking and do at least one of the following:

– add LIBDIR to the `LD_LIBRARY_PATH’ environment variable during execution
– add LIBDIR to the `LD_RUN_PATH’ environment variable during linking
– use the `-Wl,-rpath -Wl,LIBDIR’ linker flag
– have your system administrator add LIBDIR to `/etc/ld.so.conf’

See any operating system documentation about shared libraries for more information, such as the ld(1) and ld.so(8) manual pages.

———————————————————————-

编译方法(源文件为test.c):
#gcc test.c -ggdb -I/usr/local/BerkeleyDB.5.1/include/ -L/usr/local/BerkeleyDB.5.1/lib/ -ldb -lpthread -o test.out

unix系统参考指南:http://www.oracle.com/technology/documentation/berkeley-db/db/ref/build_unix/solaris.html
BerkeleyDB参考资料:http://www.oracle.com/technology/documentation/berkeley-db/db/ref/toc.html

可使用如下程序测试_______________

#include  
#include  
#include  
#include  
//only this head should include for use bdb. 
#include  
#define DATABASE "free.db" 
#define DBLOCAT "/home/free/code/cc++/new" 
 
int main() 
{ 
    DB_ENV *myEnv; 
    DB *dbp; 
    DBT key, data; 
    int ret,t_ret; 
    u_int32_t env_flags; 
    //........... Create an environment object and initialize it for error reporting 
    ret = db_env_create(&myEnv, 0); 
    if (ret != 0) 
    { 
        fprintf(stderr, "Error creating env handle: %s\n", db_strerror(ret)); 
        return -1; 
    } 
    //........If the environment does not exist create it. Initialize the in-memory cache. 
    env_flags = DB_CREATE | DB_INIT_MPOOL; 
    //........Open the environment. 
    ret = myEnv->open(myEnv,DBLOCAT,env_flags,0); 
    if (ret != 0) 
    { 
        fprintf(stderr, "Environment open failed: %s", db_strerror(ret)); 
        return -1; 
    } 
 
    if ((ret = db_create(&dbp, myEnv, 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); 
    } 
    memset(&key, 0, sizeof(key)); 
    memset(&data, 0, sizeof(data)); key.data = "sport"; 
    key.size = sizeof("sport"); 
    data.data = "football"; 
    data.size = sizeof("football"); 
    /* 
    //......put data 
    if ((ret = dbp->put(dbp, NULL, &key, &data, 0)) == 0) 
    { 
        printf("db: %s: key stored.\n", (char *)key.data); 
    } 
    else 
    { 
        dbp->err(dbp, ret, "DB->put"); 
    } 
    */ 
 
    //........put data NOOVERWRITE 
    if ((ret = dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE)) == 0) 
        printf("db: %s: key stored.\n", (char *)key.data); 
    else  
        dbp->err(dbp, ret, "DB->put"); 
 
    //.......get data 
    if ((ret = dbp->get(dbp, NULL, &key, &data, 0)) == 0) 
        printf("db: %s: key retrieved: data was %s.\n", (char *)key.data, (char *)data.data); 
    else 
        dbp->err(dbp, ret, "DB->get"); 
     //......del data 
    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"); 
    //.........close, only when the db successful closed,the data can real write to the disk. 
    //if ((t_ret = dbp->close(dbp, 0)) != 0 && ret == 0) 
    //ret = t_ret; 
    //exit(ret); 
    if (dbp != NULL) 
        dbp->close(dbp, 0); 
    //.........close evn 
    //........When you are done with an environment, you must close it. 
    //........Before you close an environment, make sure you close any opened databases 
    if (myEnv != NULL) 
        myEnv->close(myEnv, 0); 
     return 0; 
} 

Berkeley DB  C语言入门

像MySQL这类基于C/S结构的关系型数据库系统虽然代表着目前数据库应用的主流,但却并不能满足所有应用场合的需要。有时我们需要的可能只是一个简单的基于磁盘文件的数据库系统。这样不仅可以避免安装庞大的数据库服务器,而且还可以简化数据库应用程序的设计。Berkeley DB正是基于这样的思想提出来的。

Berkeley DB简介

Berkeley DB是一个开放源代码的内嵌式数据库管理系统,能够为应用程序提供高性能的数据管理服务。应用它程序员只需要调用一些简单的API就可以完成对数据的访问和管理。与常用的数据库管理系统(如MySQL和Oracle等)有所不同,在Berkeley DB中并没有数据库服务器的概念。应用程序不需要事先同数据库服务建立起网络连接,而是通过内嵌在程序中的Berkeley DB函数库来完成对数据的保存、查询、修改和删除等操作。

Berkeley DB为许多编程语言提供了实用的API接口,包括C、C++、Java、Perl、Tcl、Python和PHP等。所有同数据库相关的操作都由Berkeley DB函数库负责统一完成。这样无论是系统中的多个进程,或者是相同进程中的多个线程,都可以在同一时间调用访问数据库的函数。而底层的数据加锁、事务日志和存储管理等都在Berkeley DB函数库中实现。它们对应用程序来讲是完全透明的。俗话说:“麻雀虽小五脏俱全。”Berkeley DB函数库本身虽然只有300KB左右,但却能够用来管理多达256TB的数据,并且在许多方面的性能还能够同商业级的数据库系统相抗衡。就拿对数据的并发操作来说,Berkeley DB能够很轻松地应付几千个用户同时访问同一个数据库的情况。此外,如果想在资源受限的嵌入式系统上进行数据库管理,Berkeley DB可能就是惟一正确的选择了。

Berkeley DB作为一种嵌入式数据库系统在许多方面有着独特的优势。首先,由于其应用程序和数据库管理系统运行在相同的进程空间当中,进行数据操作时可以避免繁琐的进程间通信,因此耗费在通信上的开销自然也就降低到了极低程度。其次,Berkeley DB使用简单的函数调用接口来完成所有的数据库操作,而不是在数据库系统中经常用到的SQL语言。这样就避免了对结构化查询语言进行解析和处理所需的开销。

基本概念

Berkeley DB简化了数据库的操作模式,同时引入了一些新的基本概念,从而使得访问和管理数据库变得相对简单起来。在使用Berkeley DB提供的函数库编写数据库应用程序之前,有必要先了解以下这些基本概念。

关键字和数据

关键字(Key)和数据(Data)是Berkeley DB用来进行数据库管理的基础,由这两者构成的Key/Data对(见表1)组成了数据库中的一个基本结构单元,而整个数据库实际上就是由许多这样的结构单元所构成的。通过使用这种方式,开发人员在使用Berkeley DB提供的API来访问数据库时,只需提供关键字就能够访问到相应的数据。

| Key  | Data  |
| sport | football |
| Fruit | orange  |
| Drink | beer   |

表1 Key/Data对

如果想将第一行中的“sport”和“football”保存到Berkeley DB数据库中,可以调用Berkeley DB函数库提供的数据保存接口。此时“sport”和“football”将分别当成关键字和数据来看待。之后如果需要从数据库中检索出该数据,可以用“sport”作为关键字进行查询。此时Berkeley DB提供的接口函数会返回与之对应的数据“football”。

关键字和数据在Berkeley DB中都是用一个名为DBT的简单结构来表示的。实际上两者都可以是任意长度的二进制数据,而DBT的作用主要是保存相应的内存地址及其长度,其结构如下所示:

typedef struct
{
void *data;
u_int32_t size;
u_int32_t ulen;
u_int32_t dlen;
u_int32_t doff;
u_int32_t flags;
} DBT;

在使用Berkeley DB进行数据管理时,缺省情况下是一个关键字对应于一个数据,但事实上也可以将数据库配置成一个关键字对应于多个数据。

对象句柄

在Berkeley DB函数库定义的大多数函数都遵循同样的调用原则:首先创建某个结构,然后再调用该结构中的某些方法。从程序设计的角度来讲,这一点同面向对象的设计原则是非常类似的,即先创建某个对象的一个实例,然后再调用该实例的某些方法。正因如此,Berkeley DB引入了对象句柄的概念来表示实例化后的结构,并且将结构中的成员函数称为该句柄的方法。

对象句柄的引入使得程序员能够完全凭借面向对象的思想,来完成对Berkeley DB数据库的访问和操作,即使当前使用的是像C这样的结构化语言。例如,对于打开数据库的操作来说,可以调用DB的对象句柄所提供的open函数,其原型如下所示:

int DB->open(DB *db, DB_TXN *txnid, const char *file, const char *database, DBTYPE type, u_int32_t flags, int mode);

错误处理

对于任何一个函数库来说,如何对错误进行统一的处理都是需要考虑的问题。Berkeley DB提供的所有函数都遵循同样的错误处理原则,即函数成功执行后返回零,否则的话则返回非零值。

对于系统错误(如磁盘空间不足和访问权限不够等),返回的是一个标准的值;而对于非系统错误,返回的则是一个特定的错误编码。例如,如果在数据库中没有与某个特定关键字所对应的数据,那么在通过该关键字检索数据时就会出现错误。此时函数的返回值将是DB_NOTFOUND,表示所请求的关键字并没有在数据库中出现。所有标准的errno值都是大于零的,而由Berkeley DB定义的特殊错误编码则都是小于零的。

要求程序员记住所有的错误代号既不现实也没有什么实际意义,因为Berkeley DB提供了相应的函数来获得错误代号所对应的错误描述。一旦有错误发生,只需首先调用db_strerror()函数来获得错误描述信息,然后再调用DB->err()或DB->errx()就可以很轻松地输出格式化后的错误信息。

应用统一的编程接口

使用Berkeley DB提供的函数来进行数据库的访问和管理并不复杂,在大多数场合下只需按照统一的接口标准进行调用就可以完成最基本的操作。

打开数据库

打开数据库通常要分两步进行:首先调用db_create()函数来创建DB结构的一个实例,然后再调用DB->open()函数来完成真正的打开操作。Berkeley DB将所有对数据库的操作都封装在名为DB的结构中。db_create()函数的作用就是创建一个该结构,其原型如下所示:

typedef struct__db DB;
int db_create(DB **dbp, DB_ENV *dbenv, u_int32_t flags);

将磁盘上保存的文件作为数据库打开是由DB->open()函数来完成的,其原型如下所示:

int DB->open(DB *db, DB_TXN *txnid, const char *file, const char *database, DBTYPE type, u_int32_t flags, int mode);

下面这段代码示范了如何创建DB对象句柄及如何打开数据库文件:

#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <db.h>
#define DATABASE “demo.db”

/* 以下程序代码的程序头同此*/
int main()
{
DB *dbp;
int ret;
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);
}
}

代码首先调用db_create()函数来创建一个DB对象句柄。变量dbp在调用成功后将成为数据库句柄,通过它可以完成对底层数据库的配置或访问。接下去调用DB->open()函数打开数据库文件,参数“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);

下面这段代码示范了如何向数据库中添加新的数据:

int main()
{
DB *dbp;
DBT key, data;
int ret;
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);
}
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
key.data = “sport”;
key.size = sizeof(“sport”);
data.data = “football”;
data.size = sizeof(“football”);
if ((ret = dbp->put(dbp, NULL, &key, &data, 0)) == 0)
printf(“db: %s: key stored.\n”, (char *)key.data);
else
dbp->err(dbp, ret, “DB->put”);
}

代码首先声明了两个DBT结构变量,并分别用字符串“sport”和“football”进行填充。它们随后作为关键字和数据传递给用来添加数据的DB->put()函数。DBT结构几乎会在所有同数据访问相关的函数中被用到。

在向数据库中添加数据时,如果给定的关键字已经存在,大多数应用会对于已经存在的数据采用覆盖原则。也就是说,如果数据库中已经保存了一个“sport/basketball”对,再次调用DB->put()函数添加一个“sport/football”对,那么先前保存的那些数据将会被覆盖。但Berkeley DB允许在调用DB->put()函数时指定参数“DB_NOOVERWRITE”,声明不对数据库中已经存在的数据进行覆盖,其代码如下:

if ((ret = dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE)) == 0)
printf(“db: %s: key stored.\n”, (char *)key.data);
else
dbp->err(dbp, ret, “DB->put”);

一旦给出“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);

下面这段代码示范了如何从数据库中检索出所需的数据:

int main()
{
DB *dbp;
DBT key, data;
int ret;
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);
}
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
key.data = “sport”;
key.size = sizeof(“sport”);
if ((ret = dbp->get(dbp, NULL, &key, &data, 0)) == 0)
printf(“db: %s: key retrieved: data was %s.\n”, (char *)key.data, (char *)data.data);
else
dbp->err(dbp, ret, “DB->get”);
}

代码同样声明了两个DBT结构变量,并且调用memset()函数对它们的内容清空。虽然Berkeley DB并不强制要求在进行数据操作之前先清空它们,但出于提高代码质量考虑还是建议先进行清空操作。在进行数据检索时,对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);

下面这段代码示范了如何从数据库中删除数据:

int main()
{
DB *dbp;
DBT key;
int ret;
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);
}
memset(&key, 0, sizeof(key));
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);

下面这段代码示范了如何在需要的时候关闭数据库:

int main()
{
DB *dbp;
DBT key, data;
int ret, t_ret;
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);
goto err;
}
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
key.data = “sport”;
key.size = sizeof(“sport”);
if ((ret = dbp->get(dbp, NULL, &key, &data, 0)) == 0)
printf(“db: %s: key retrieved: data was %s.\n”, (char *)key.data, (char *)data.data);
else
dbp->err(dbp, ret, “DB->get”);
if ((t_ret = dbp->close(dbp, 0)) != 0 && ret == 0)
ret = t_ret;
exit(ret);
}

小结

Berkeley DB这个嵌入式数据库系统使用非常简单。它没有数据库服务器的概念,也不需要复杂的SQL语句,所有对数据的操作和管理都可以通过函数调用来完成,非常适合于那些需要对数据进行简单管理的应用场合。

发表评论