berkeley.docx
《berkeley.docx》由会员分享,可在线阅读,更多相关《berkeley.docx(39页珍藏版)》请在冰豆网上搜索。
berkeley
1楼发表于2009-06-2515:
42|只看该作者
BerkeleyDB是一个开源的文件数据库,介于关系数据库与内存数据库之间,使用方式与内存数据库类似,它提供的是一系列直接访问数据库的函数,而不是像关系数据库那样需要网络通讯、SQL解析等步骤。
BerkeleyDB函数库早期版本只有300K大小,但却可管理高达256TB的数据,现在的最新版4.7也只有几MB,既能在小型的嵌入式设备上使用,也可以在大型设备上管理重要的数据。
它目前已经应用在Apache、MySQL、Sendmail、Subversion、OpenLDAP、Mozilla、Google等很多地方,而在MySQL数据库中担任的更是核心数据处理引擎,使MySQL成为一个小型的快速的关系数据库,不过从MySQL5.1版本开始不再使用BerkeleyDB,因为它已经被Oracle以10亿美金之巨收归囊中,而MySQL也已进了Sun的家门。
BerkeleyDB对C、C++、Perl、Java、Python、Ruby、PHP等基本上所有的语言都提供了接口,对一条记录只分为两个字段,一个为键,一个为值,键与值可以是任意的数据,并且可以长达4GB,它提供了四种数据存取算法:
B+树、Hash、Recno、Queue,根据不同数据类型,可以选择适当的算法以达到最佳性能。
BerkeleyDB可以轻松支持上千个线程同时访问数据库,支持多进程、事务等特性。
BerkeleyDB历史:
1991年,BerkeleyDB的第一个版发行(Linux系统也是在这一年诞生)。
1992年,BSDUNIX第4.4发行版中包含了BerkeleyDB1.85版。
基本上认为这是BerkeleyDB的第一个正式版。
1996年,Sleepycat软件公司成立,提供对BerkeleyDB的商业支持。
2006年,Sleepycat被Oracle收购,当前最新版本是4.7.25。
2009年,SUN被Oracle收购,不知道MySQL会不会再次启用BerkeleyDB。
BerkeleyDB
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算法(extendedlinearhashing),可以根据HASH表的增长进行适当的调整。
关键字可以为任意的数据结构。
Recno算法:
要求每一个记录都有一个逻辑纪录号,逻辑纪录号由算法本身生成。
实际上,这和关系型数据库中逻辑主键通常定义为intAUTO型是同一个概念。
Recho建立在B+树算法之上,提供了一个存储有序数据的接口。
记录的长度可以为定长或不定长。
Queue算法:
和Recno方式接近,只不过记录的长度为定长。
数据以定长记录方式存储在队列中,插入操作把记录插入到队列的尾部,相比之下插入速度是最快的。
对算法的选择首先要看关键字的类型,如果为复杂类型,则只能选择B+树或HASH算法,如果关键字为逻辑记录号,则应该选择Recno或Queue算法。
当工作集关键字有序时,B+树算法比较合适;如果工作集比较大且基本上关键字为随机分布时,选择HASH算法。
Queue算法只能存储定长的记录,在高的并发处理情况下,Queue算法效率较高;如果是其它情况,则选择Recno算法,Recno算法把数据存储为平面文件格式。
创建句柄:
DB*dbp; /*DBstructurehandle*/
intret; /*functionreturnvalue*/
/*db_create()相当于C++接口中的构造函数*/
ret=db_create(&dbp,NULL,0); /*Initializethestructure.This
*databaseisnotopenedinanenvironment,
*sotheenvironmentpointerisNULL.*/
打开数据库:
ret=dbp->open(dbp, /*DBstructurepointer*/
NULL, /*Transactionpointer*/
"test.db", /*On-diskfilethatholdsthedatabase.*/
NULL, /*Optionallogicaldatabasename*/
DB_BTREE, /*Databaseaccessmethod*/
flags, /*Openflags*/
0); /*Filemode(usingdefaults)*/
常用的flags有DB_CREATE、DB_EXCL、DB_RDONLY、DB_TRUNCATE
删除数据库:
dbp->remove(dbp, /*Databasepointer*/
"test.db", /*Databasefiletoremove*/
NULL, /*Databasetoremove.Thisis
*NULLsotheentirefileis
*removed.*/
0); /*Flags.Noneused.*/
更名数据库:
dbp->rename(dbp, /*Databasepointer*/
"test.db", /*Databasefiletorename*/
NULL, /*Databasetorename.Thisis
*NULLsotheentirefileis
*renamed.*/
"newname.db", /*Newdatabasefilename*/
0); /*Flags.Noneused.*/
注:
删除、更名数据库后不能打开,否则会段错误
错误处理:
printf("%s\n",db_strerror(ret));/*db_strerror()*/
打开环境:
DB_ENV*myEnv; /*Envstructurehandle*/
DB*dbp; /*DBstructurehandle*/
u_int32_tdb_flags; /*databaseopenflags*/
u_int32_tenv_flags; /*envopenflags*/
intret; /*functionreturnvalue*/
ret=db_env_create(&myEnv,0); /*Createanenvironmentobject*/
env_flags=DB_CREATE| /*Iftheenvironmentdoesnotexist,
createit.*/
DB_INIT_MPOOL; /*Initializethein-memorycache.*/
ret=myEnv->open(myEnv, /*DB_ENVptr*/
"/export1/testEnv", /*envhomedirectory*/
env_flags, /*Openflags*/
0); /*Filemode(default)*/
ret=db_create(&dbp,myEnv,0);
增加记录:
DBTkey,data;
floatmoney=122.45;
char*description="Grocerybill.";
/*ZeroouttheDBTsbeforeusingthem.*/
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("Putfailedbecausekey%falreadyexists",money);
}
获取记录:
floatmoney;
DBTkey,data;
char*description;
/*InitializetheDBTs*/
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为用户的缓冲区大小,要大于等于实际记录的长度*/
/*Databaseretrievalcodegoeshere*/
/*
*Moneyissetintothememorythatwesupplied.
*/
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();
/*InitializeourDBTs.*/
memset(&key,0,sizeof(DBT));
memset(&data,0,sizeof(DBT));
/*Iterateoverthedatabase,retrievingeachrecordinturn.*/
while((ret=cursorp->c_get(cursorp,&key,&data,DB_NEXT))==0){
/*DointerestingthingswiththeDBTshere.*/
}
if(ret!
=DB_NOTFOUND){
/*Errorhandlinggoeshere*/
}
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();
/*Iterateoverthedatabase,deletingeachrecordinturn.*/
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标志,这个做法对重复记录可能存在一些问题,因此可以使用先删除,后增加的方式来更新记录。
*/
副数据库:
/*Nowassociatethesecondarytotheprimary*/
dbp->associate(dbp, /*Primarydatabase*/
NULL, /*TXNid*/
sdbp, /*Secondarydatabase*/
get_sales_rep, /*Callbackusedforkeycreation.Not
*definedinthisexample.Seethenext
*section.*/
0); /*Flags*/
回调函数:
int
get_sales_rep(DB*sdbp, /*secondarydbhandle*/
constDBT*pkey, /*primarydbrecord'skey*/
constDBT*pdata, /*primarydbrecord'sdata*/
DBT*skey) /*secondarydbrecord'skey*/
{
return0;
}
副数据库只能手工查找、删除记录,不能修改或增加记录,若需要修改记录,直接修改主数据库中的记录,然后通过回调函数自动完成对副数据库的修改。
数据库管理:
获取数据库统计信息:
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(),默认用字典顺序比较
默认不支持重复记录,相同的记录采用覆盖掉现有的记录。
BerkeleyDB
像mysql这类基于c/s结构的关系型数据库系统虽然代表着目前数据库应用的主流,但却并不能满足所有应用场合的需要。
有时我们需要的可能只是一个简单的基于磁盘文件的数据库系统。
这样不仅可以避免安装庞大的数据库服务器,而且还可以简化数据库应用程序的设计。
berkeleydb正是基于这样的思想提出来的。
berkeleydb简介
berkeleydb是一个开放源代码的内嵌式数据库管理系统,能够为应用程序提供高性能的数据管理服务。
应用它程序员只需要调用一些简单的api就可以完成对数据的访问和管理。
与常用的数据库管理系统(如mysql和oracle等)有所不同,在berkeleydb中并没有数据库服务器的概念。
应用程序不需要事先同数据库服务建立起网络连接,而是通过内嵌在程序中的berkeleydb函数库来完成对数据的保存、查询、修改和删除等操作。
berkeleydb为许多编程语言提供了实用的api接口,包括c、c++、java、perl、tcl、python和php等。
所有同数据库相关的操作都由berkeleydb函数库负责统一完成。
这样无论是系统中的多个进程,或者是相同进程中的多个线程,都可以在同一时间调用访问数据库的函数。
而底层的数据加锁、事务日志和存储管理等都在berkeleydb函数库中实现。
它们对应用程序来讲是完全透明的。
俗话说:
“麻雀虽小五脏俱全。
”berkeleydb函数库本身虽然只有300kb左右,但却能够用来管理多达256tb的数据,并且在许多方面的性能还能够同商业级的数据库系统相抗衡。
就拿对数据的并发操作来说,berkeleydb能够很轻松地应付几千个用户同时访问同一个数据库的情况。
此外,如果想在资源受限的嵌入式系统上进行数据库管理,berkeleydb可能就是惟一正确的选择了。
berkeleydb作为一种嵌入式数据库系统在许多方面有着独特的优势。
首先,由于其应用程序和数据库管理系统运行在相同的进程空间当中,进行数据操作时可以避免繁琐的进程间通信,因此耗费在通信上的开销自然也就降低到了极低程度。
其次,berkeleydb使用简单的函数调用接口来完成所有的数据库操作,而不是在数据库系统中经常用到的sql语言。
这样就避免了对结构化查询语言进行解析和处理所需的开销。
基本概念
berkeleydb简化了数据库的操作模式,同时引入了一些新的基本概念,从而使得访问和管理数据库变得相对简单起来。
在使用berkeleydb提供的函数库编写数据库应用程序之前,有必要先了解以下这些基本概念。
关键字和数据
关键字(key)和数据(data)是berkeleydb用来进行数据库管理的基础,由这两者构成的key/data对(见表1)组成了数据库中的一个基本结构单元,而整个数据库实际上就是由许多这样的结构单元所构成的。
通过使用这种方式,开发人员在使用berkeleydb提供的api来访问数据库时,只需提供关键字就能够访问到相应的数据。
key data
sport football
fruit orange
drink beer
表1key/data对
如果想将第一行中的“sport”和“football”保存到berkeleydb数据库中,可以调用berkeleydb函数库提供的数据