Berkeley DB 发布新版本
Berkeley DB 4.8 新特性
经过了一年多的辛勤工作和汗水,Berkeley DB 4.8版(以下简称为DB 4.8 或者 4.8)终于发布了。下文我将概括介绍这一版本带来的一些新的特性和提升,我想 读完后您有理由相信这又会是一个激动人心的新版本。当然,具体技术细节及文 档,请参照我们的官方网站的信息(此博客的侧边栏有链接)。同时,我在此也感 谢您的关注和支持。记得留下脚印哦。
下面我就对照Berkeley DB 4.8的change log中的新特性一节来概括介绍一下。
1. B树的共享栓
所谓栓,是指一种资源,类似于mutex,是一种轻量级的锁。在4.7中,访问一个页 的数据时,需要先锁住页(page),访问完后再释放锁。而在4.8 中,我们提出了的 共享栓的概念,即当多个线程/进程并发访问同一个页的时候,尽量彼此使用共享 的栓,从而避免等待。从我们的在Solaris 10上测试结果来看,如果线程数 >= CPU数,4.7的性能下降较多;而使用的共享栓的4.8,速度比同样情况下4.7的提升 25%-16倍左右。当然,具体性能提升指标取决于应用场景和平台。
2. 提供了C# API
现在,做.net开发的用户,可以通过调用4.8的C# API 来使用Berkeley DB了。C# API 运行于Windows 32位和64位平台,要求有.net framework 2.0及以上版本。 4.8发布版本还附带了很多C#的示例和测试程序,可供广大使用者参考。具体详 情,可以关注BDB中国开发团队的Emily的博客。
3. 基于BDB 实现了C++ 中STL(集合)的功能
我想,几乎每个C++的用户都对STL中的易用性留有印象。而BDB本身又是一个 Key/Value的数据库,非常类似于集合中的 Map,那么能不能给予BDB来实现一些集 合的功能呢?BDB中国开发团队的David在过去一年中,在BDB的C++ API之上实现了 一套STL的接口。简言之,David把BDB包装成了Map, Set, Vector给开发者使用。 好处是,你的应用程序可以利用BDB提供的本地的持久化,事务,并发,错误恢复 等功能,而且性能又得以保证。也许有人会问,我直接使用C++ STL就够了,为什 么还要这么麻烦?试想,当你的应用程序的数据集很大,超出了你可用的内存的时 候,你还能用C++ 的STL吗?并发访问性能如何?如何提供事务的ACID?够了,我 想足够说服北京奥运在线售票系统开发的那帮家伙了吧?你不想你的商业应用,因 为同时在线订票的客户太多而导致系统下线吧?开个玩笑,跑题了…
4. 自动分区
我想做DBA的,尤其是Oracle DBA很多都熟悉分区。我们以关系数据库来举例说 明,假设有一张表,记录了全国13亿人的信息。平均每个人的信息量为100 kb (算 上数码照片),那么一共需要多大的磁盘空间?13亿 * 100 kb ~= 130 Terra bytes. 啥概念?约需要130个,每个容量为1T字节的硬盘。所以,我可以把这张表 分区 – 比如:姓张的存在第一个磁盘,姓李的放在第二个磁盘,… 当然,具体 分区的规则有很多,你可能根据你的应用来做相应调整。在BDB里面,可以通过DB ->set_partition(), DB->set_partition_dirs() 和 DB_ENV->add_data_dir()等 API来实现分区的目的。
5. 批量插入和批量删除
众所周知,通过BDB API读取记录时,可以每次读一条也可以批量读取。在4.8里, 我们又提供了批量插入和删除的功能,从而来提高效率。批量查询可以通过设置DB_MULTIPLE or DB_MULTIPLE_KEY in DBC->get 来实现;而在DB->put() & DB->del() 中加上 DB_MULTIPLE 和 DB_MULTIPLE_KEY 标志来实现批量插入和删除。具体性能提升指 标取决于应用场景和平台。
6. B树索引的压缩
通过对基于B树的索引进行压缩,可以达到减少索引占有的空间大小,进而减少IO 次数来提高性能。具体使用为:在DB->set_compress () 指定压缩/解压缩的 callback 函数。
7. 增加了DB_SQL的工具
4.8提供了一个新的工具,叫DB_SQL,意即它将用户的SQL DDL转换成具体的BDB的 API调用。这样,对于SQL开发者而言,他们只需用提供SQL DDL的脚本,而后通过 运行DB_SQL就可以生成具体的BDB的代码。听上去有点像关系型数据库的SQL解析, 对吗?具体细节,大家可以关注Emily 的后续博客中的介绍。
8. 集群功能的增强
在早期版本中,当多个进程需要访问Master的Environment时,必须要通过BASE API来访问。现在,在Master节点上,可以通过Replication Manager来提供多进程 共享访问Master Environment了。
9. 增加了多个database间外键的约束
同关系型数据库中,在多个表之间建立外键约束一样,DB现在也支持在多个 database之间通过建立外键实现参照完整性约束。举例来讲,有雇员表(在 DB 中,称为employee database)和部门表(DB中叫department database),它们通 过部门编号字段来建立外键。对照于DB,您可以调用DB->associate_foreign()来 建立雇员表到部门表的外键约束。通过它(外键)就可以保证:1)向雇员表中插 入记录时,每个员工的部门编号都在部门表中;2)当删除某个部门时(从部门表 删除),该部门对应的员工的部门号字段都会做自动调整:DB_ABORT, DB_CASCADE 和 DB_NULLIFY 来指定。
10. 对DB_REGISTER & DB_ENV->failchk()的增强
具体请参考发布版的文档。
11. 100% 在内存中的集群(replication)
对于HA(High Available,高可用)的应用而言,现在4.8提供了更多的持久化方 案。你可以选择基于磁盘的持久化,100%在内存的持久化以及前面二者的混合方 案。具体可通过DB_ENV->rep_set_config()中指定DB_REP_CONF_INMEM来实现。好 处是显而易见的,潜在的风险是对于Master die后的election和事务回退的风险更高。
12. 两个游标等值比较
两个游标等值比较增加了判断两个cursor是 否指向同一个数据库中的同一个key/data pair的API: DBC->cmp
@林林
不好意思这么久才给你回复,这几天太忙了。 我刚才把你的所有回复看了一遍,都是关于dbstl的你的CDbArchive+serialize接口的idea的,很感谢你的兴趣和提议,我再此全部回复,就不一一回复了。
正如我上一个回复所说,这样做虽然很简洁,但是,侵入性太大,对于第三方类(你无法修改),以及原生指针类型(wchar_t*, char*等)不适用。而dbstl需要一种普遍使用的方法解决这个问题,所以我最终选择了使用DbstlElemTraits这样一个类模板的方法。
欢迎继续讨论。如果你有更多的idea,请在这里贴出来,谢谢!
@davidzhao
代码的侵入性太大?
可是在您的例子里代码侵入性更大吧?
class SMSMsg : public BaseMsg
{
public:
size_t mysize;
size_t szmsg;
char msg[1];
static SMSMsg* make_sms_msg(time_t t, const char*msg, int dest)
{
size_t mlen = 0, totalsz = 0;
SMSMsg *p = (SMSMsg *)DbstlMalloc(totalsz = (sizeof(SMSMsg) + (mlen = strlen(msg) + 4)));
memset(p, 0, totalsz);
// adding sizeof(p->to) to avoid memory alignment issues
p->mysize = sizeof(SMSMsg) + mlen;
p->when = t;
p->szmsg = mlen – 3;
p->to = dest;
p->from = 0;
strcpy(&(p->msg[0]), msg);
return p;
}
SMSMsg()
{
}
protected:
SMSMsg(time_t t, const char*msg1, int dest)
{
size_t mlen = 0;
when = t;
szmsg = strlen(msg1) + 1;
mlen = strlen(msg1);
strncpy((char*)&(this->msg[0]), msg1, mlen);
*(int*)(((char*)&(this->msg[0])) + mlen + 1) = dest;
}
};// SMSMsg
在这个结构里您添加了两个多余的变量size_t mysize与size_t szmsg; 当然您可以说这两个变量也可以象SMSMsgRestore()函数那样定义成全局的,但是我的serialize函数同样也可以设计成全局的呀。比如取一个唯一的函数名cdbactive& testserialize(cdbactive& a);然后再通过DbstlElemTraits::instance()->set_serialize_function()去设置函数指针,也比您的方式简洁多了吧?
程序已经通过邮件发到david.zhao at oracle dot com
与chao.huang at oracle dot com
至于wchar_t*, char*这两种类型,我没有在dbserialize类里实现唯一的理由是由于它们是纯指针类型,如果在dbserialize类里申请内存,然后让用户自己释放,则违反了在哪里申请内存在哪里释放的原则,如果让用户申请内存后传进来,则破坏了dbserialize类接口调用方式的统一性。显得不优雅。而不是无法实现。
@winterzhang
你好!
由於先前問你的問題一直沒有得到回覆,
所以再問一次,希望你十一長假後看到能儘快回覆我,謝謝!
原先給你的回應訊息如下:
——————————————————————
剛剛下了db-4.7.25.zip重新編譯, 是可以正常編譯成功的!
請問一下怎麼提供config.log給你? 謝謝!
我可以提供4.7.25與4.8.24的config.log還有4.8.24出錯的error msg給你
——————————————————————
可以发邮件给我:winter.zhan@oracle.com, 谢谢。
你最初指出,db-4.8 configure是通过的, 所以希望提供以下信息:
1 config.log
2 Makefile
3 Mingw的版本,gcc的版本,是用msys还是cygwin,或者SFU。
你好,我在使用dbstl中遇到一些问题,希望能解答一下:
1.在对db_map进行操作中,会自动写日志文件吧?(或者我理解错了,没有写日志)但官网怎么没相关文档说明?也不知怎么写的?
2.我看了以上的所有评论,我也遇到了存struct,class这些对象的问题,并且采用了注册回调函数的方法(参看了官方技术文档)。但是编译有错误。举个例子:
官网明明是这样的定义:
typedef void (*SequenceCopyFunct)(RGB*dest, const RGB*src);
但编译时说转换错误,给出的合适类型却是这样:
‘void dbstl::DbstlElemTraits::set_sequence_copy_function(void (*)(T*, const T*, size_t)) [with T = datac]’
后面还有个参数size_t.我就确实不知为什么是不一样了?或是哪儿理解错了?
真诚的希望能帮助解答,谢谢!
@lz
请按照文档来吧。我相信你自己可以解决的。再不行,请post到OTN 论坛 – 地址见侧边栏。
在.net下 put的时候 怎么设置 可以添加重复的key和data
现在我添加重复key 可以 但是 key和data都重复 就报错了 难道不支持吗?
我想实现 添加重复key 和data 覆盖原先来值 怎么实现呀
@zhu
把你的问题转到OTN forum吧。
你的问题并不复杂,你也可以search 一下我们forum上的回答,从而全面了解BDB。换句话说:我告诉你答案,不如告诉你如何找答案 – 怎么诊断问题,怎么fix等。
@chaohuang
请问下 5. 批量插入和批量删除 4. 自动分区
C# API里面 4.8版本有这个功能吗?
并行存储支持多个读者多个写者不?不知道哪儿能找到example
@zhu
btreeConfig.Duplicates = DuplicatesPolicy.SORTED;
BDB 的资料有些少,其它语言的版本可参考C的例子,英语不好的用google把文档翻译一下。结合起来看会让你找到答案。也许我的学习方法有些笨,不过你们可以借鉴一下。找到适合自己的学习方法就可以了。
您好,我使用的就是Berkeley DB 4.8.24,但有一个问题,就是关于当次索引支持多key重复且有序(BD_DUPSORT)时,我设的比较函数总是调不到。请问这可能是什么原因?具体如下:
以下程序是在visual stdio 2008上编译的,主要是一个主数据库与一个次数据库,其中次数据库支持重复(DB_DUPSORT),比较函数由自己设(不使用系统的)但在实际测试中不会调自己设的回调函数(secondary_compare)来进行次索引的比较,但是如果次索引的字段改成u_char(本程序中用的为u_long)则会调到,这是为什么呢?有没有人知道的?
#include “db.h”
#include
typedef struct _testID
{
u_long PIndex;
u_long stime;
}TID;
u_long tmp;
//主索引比较函数
int primary_compare( DB* db, const DBT *key1, const DBT *key2)
{
u_long a = ((TID*)(key1 ->data))->PIndex;
u_long b = ((TID*)(key2 ->data))->PIndex;
return (a – b);
}
// secondary compare function
int secondary_compare( DB* db, const DBT *key1, const DBT *key2)
{
//在这个函数打上断点怎么老是执行不到?是不是前面我哪里设错了?
u_long a = *(u_long*)(key1->data);
u_long b = *(u_long*)(key2->data);
return (a – b);
}
// get
int ExtSKEYC(DB* dbp, const DBT* pkey, const DBT *pdata, DBT *skey)
{
skey->data = &((TID*)(pkey ->data))->stime;
skey->size = sizeof(u_long);
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
DB* db_primary = NULL;
DB* db_secondary = NULL;
// PRIMARY DB
int nRet = -1;
// CREATE
nRet = db_create(&db_primary, NULL, 0);
// COMPARE
nRet = db_primary->set_bt_compare(db_primary, primary_compare);
// open
nRet = db_primary->open(db_primary, NULL, “primary.db”, NULL, DB_BTREE, DB_CREATE,0);
// SECONDARY DB
nRet = db_create(&db_secondary, NULL, 0);
// flag
nRet = db_secondary->set_flags(db_secondary, DB_DUPSORT );
// compare
nRet = db_secondary->set_dup_compare(db_secondary, secondary_compare);
// open
nRet = db_secondary->open(db_secondary, NULL, “secondary.db”, NULL, DB_BTREE, DB_CREATE, 0);
// associate
nRet = db_primary->associate(db_primary, NULL, db_secondary, ExtSKEYC, 0);
//以上每一步操作的 返回值即 nRet 都为 0,我在调试的时候确认过!
// write
DBT key, data;
TID pindex = {0,0};
for( int i = 0; i set_dup_compare”;
data.size = strlen(“DB->set_dup_compare”) + 1;
nRet = db_primary->put( db_primary, NULL, &key, &data, 0);
}
getchar();
nRet = db_secondary->close(db_secondary, 0);
nRet = db_primary->close(db_primary, 0);
return 0;
}
二、还有就是一个主数据库加的次索引越多在存储的时候是不是越慢呢?会减慢的速率比例大约是多少?有没有什么技巧可以尽量提高速率?
三、另外就是环境目录的设置,我按照BerkeleyDB提供的开发文档,上的介绍,设置环境变量 DB_HOME =…..;但是环境目录总是设不成功,而BDB提供的DB_CONFIG文件里面好像没有关于环境目录的配置,所以只能自已写配置文件,请问有没有什么方法可以配置环境目录的?
四、关于多进程(或线程)中环境的移除问题到底是怎么一回事,我一直没有很好的明白,到目前为止都是当每个线程结束或开启时都将环境移除一次,这样会不会影响访问的效率?
谢谢!!!
您好,我使用的版本是 BerkeleyDB 4.8.24 有几个问题请教一下!
一、还有就是一个主数据库加的次索引越多在存储的时候是不是越慢呢?会减慢的速率比例大约是多少?有没有什么技巧可以尽量提高速率?
二、另外就是环境目录的设置,我按照BerkeleyDB提供的开发文档,上的介绍,设置环境变量 DB_HOME =…..;但是环境目录总是设不成功,而BDB提供的DB_CONFIG文件里面好像没有关于环境目录的配置,所以只能自已写配置文件,请问有没有什么方法可以配置环境目录的?
三、关于多进程(或线程)中环境的移除问题到底是怎么一回事,我一直没有很好的明白,到目前为止都是当每个线程结束或开启时都将环境移除一次,这样会不会影响访问的效率?
谢谢!!!
您好,关于 BerkeleyDB 的次索引多key重复且有序(DB_DUPSORT),并且比较函数是自己设的,次索引字段的类型为 u_long 。在运行是每一步我检查都是正确的,但就是我自己设的比较函数不会调用,程序只调用系统,请问是怎么回事?对次索引我使用了,
sdb ->set_flag(sdb, DB_DUPSORT);
sdb ->set_dup_compare(sdb, sdbcompare);
来实现支持多key重复且排序,然后 sdb ->open(..)以后,最后关联
pdb ->associate(..);
请问是不我哪里没用对呀?
谢谢~~~
新建的一个群 46851684,大家在这里讨论不如我们在线交流,把大家的经验共享,共同进步!更快更好的家习!
您好,我在做一个bdb的性能测试。我想插入1亿条数据(都是32字节的md5值),但是无论用什么方式,性能都不算理想。
我自己测试的结果是,用很大的内存缓存,插入速度很快;数据库文件超过缓存大小时, 性能都很低(而且似乎”缓存写“的效果没有得到体现)。
具体测试数据:hash 方式, 200M内存缓存;每三百万条数据执行一次加载进程;
结果:前1200万数据(前4个进程)加载了3小时;再后来的1200万数据加载了41小时;
之前用btree方式测试也不理想;
我想问一下,BDB加载大记录量有没有什么限制?能不能较快的处理1亿条数据?有没有快速批量导入的方法呢?
现在我的测试结果看,一次性载入大量记录的性能,和mysql相差很远。
当然,我期望如果没意外的话, 加载完1亿条数据以后,再插入单条数据时,性能比mysql快。
把你的测试程序贴上来看看。 在大批量数据插入的时候要达到高性能需要一些细致的调优配置。
需要注意哪些调优配置呢?
我初用BDB, 就用了最简单的AccessExample的例子, 增加了如下配置:
DbEnv env(0);
DbEnv * p_env = NULL;
env.set_error_stream(&cerr);
env.set_errpfx(“Db4Uniq”);
env.set_cachesize(0, 200 * 1024 * 1024, 0);
env.open( dirName,
DB_INIT_CDB
| DB_INIT_MPOOL
| DB_CREATE ,
0664 );
p_env = &env;
Db db(p_env, 0);
db.set_pagesize(1024);
if(type == DB_HASH) {
db.set_h_nelem(600000000);
}
@dulao5
之所以称BerkeleyDB为内存数据库就是因为它是个毫内存的东西!如果数据达到内存的大小之后插入的数据肯定会慢,这是正常的。berkeleydb本身对数据的大小是没有什么限制的至少几个T的数据是不成问题的,可以说它完全取决于你的pc机的位数,以及页面尺寸和内存的大小!其实berkeley db本身提供了很多的api,要完成强大的功能很大程度上是依赖于设计者本身对程序的设计!如你上面所说的情况,你可以另起一个线程来同步数据由缓冲到文件。你也可以在一个环境中设置多个数据库每然后交替来打开关闭进行数据的插入,只要你把握好他们所占内存的比例!还有berkeley db的配置也很重要(像上面那位仁兄说的)如:页尺寸的大小,每页的最小key值等都会影响插入的响率!
berkeleydb是支持批量插入的!但有一点要注意似乎批量插入时只能用DB的put方法不能使用游标!
还是那句话要完成强大的功能还是在于程序本身的设计。像上面那位仁兄说的你最好能把你的程序贴上来大家一起来给你研究一下!!
xiaoxin, 您好
非常感谢你的回复!
我初用BDB, API都还不熟悉, (BDB真是博大精深啊) , 我的代码很简单,我放在了google docs上: http://docs.google.com/View?id=df4f2fzq_18dk4qmzhh。
用法:
uniq_by_db4 -f hash.dat -d ./ -t hash output.txt
data.txt是输入文件,每行一条记录(我这里是32字节的md5值)。
你的回复对我很有启发,我查到“DbEnv::memp_sync()” 这个API,似乎是用来同步的,准备测试一下效果。
似乎这个API需要环境建立DbMpoolFile? 我还没太明白。
我期望定期将cache数据写入磁盘后, 程序的cache能够得到释放, 再继续写入时速度能够加快。不知道这样期望是不是正确的?
另外, 我实际测试时, 是每批数据执行一个进程。我期望一批数据载入完毕后,进程退出、cache释放,下一个进程载入时将新书具载入cache, 这样速度能够加快。但是实际结果不符合预期,所以很奇怪。
以上是我的疑问, 希望您有空能帮我看一下,感谢您了!
请问出现以下问题怎么解决?
/usr/local/berkeleydb/c1.cc:11: undefined reference to `Db::Db(DbEnv*, unsigned int)’
/usr/local/berkeleydb/c1.cc:13: undefined reference to `Db::open(DbTxn*, char const*, char const*, DBTYPE, unsigned int, int)’
/usr/local/berkeleydb/c1.cc:19: undefined reference to `Dbt::Dbt(void*, unsigned int)’
/usr/local/berkeleydb/c1.cc:20: undefined reference to `Dbt::Dbt(void*, unsigned int)’
/usr/local/berkeleydb/c1.cc:23: undefined reference to `Db::put(DbTxn*, Dbt*, Dbt*, unsigned int)’
/usr/local/berkeleydb/c1.cc:29: undefined reference to `Dbt::Dbt()’
/usr/local/berkeleydb/c1.cc:30: undefined reference to `Db::get(DbTxn*, Dbt*, Dbt*, unsigned int)’
/usr/local/berkeleydb/c1.cc:34: undefined reference to `Db::close(unsigned int)’
/usr/local/berkeleydb/c1.cc:34: undefined reference to `Dbt::~Dbt()’
/usr/local/berkeleydb/c1.cc:34: undefined reference to `Dbt::~Dbt()’
/usr/local/berkeleydb/c1.cc:34: undefined reference to `Dbt::~Dbt()’
/usr/local/berkeleydb/c1.cc:34: undefined reference to `Dbt::~Dbt()’
/usr/local/berkeleydb/c1.cc:34: undefined reference to `Dbt::~Dbt()’
Debug/c1.o:/usr/local/berkeleydb/c1.cc:34: more undefined references to `Dbt::~Dbt()’ follow
Debug/c1.o: In function `main’:
/usr/local/berkeleydb/c1.cc:34: undefined reference to `Db::~Db()’
/usr/local/berkeleydb/c1.cc:34: undefined reference to `Db::~Db()’
Debug/c1.o:(.gcc_except_table+0×50): undefined reference to `typeinfo for DbException’
collect2: ld returned 1 exit status
*** Errors occurred during this build ***
@fifaangel
你好,这是一个链接错误。请检查链接的时候是否正确链接了Berkeley DB的库(指定-ldb).
我们的项目中正在应用BDB,需要建二级索引,用来实现分组查询后的分页,请问BDB本身有没有什么办法可以解决这个问题?
你好,BDB可以帮助应用程序实现这个功能,应用程序根据自身的需要,设计应用逻辑相关的实现。建立二级索引后,可以用BDB批量查询的方法每次定量提取记录。例如,假设每条记录长度是100字节,设置一个1024字节大小的缓冲,每次可以提取满足条件的10条记录,应用程序处理后,可继续提取直到提取完所有满足条件的记录为止。
BDB批量处理目前仅提取连续数据记录。若应用程序需要提取不连续的数据记录,建议做 一些预处理或者后处理,以得到期望的分页结果。
具体请参考BDB批量处理:http://download.oracle.com/docs/cd/E17076_02/html/programmer_reference/am_misc_bulk.html