社区应用 最新帖子 精华区 社区服务 会员列表 统计排行 银行

  • 68401阅读
  • 72回复

NoSql数据库技术文章汇总(leveldb, Berkeley DB,sqlite)

级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
— 本帖被 usidc5 执行加亮操作(2012-04-05) —

Berkeley DB 入门


Berkeley DB是一个开源的文件数据库,介于关系数据库与内存数据库之间,使用方式与内存数据库类似,它提供的是一系列直接访问数据库的函数,而不是像关系数据库那样需要网络通讯、SQL解析等步骤。
  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,根据不同数据类型,可以选择适当的算法以达到最佳性能。
  Berkeley DB可以轻松支持上千个线程同时访问数据库,支持多进程、事务等特性。
  Berkeley DB历史:
    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。


Berkeley DB
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算法(extended linear hashing),可以根据HASH表的增长进行适当的调整。关键字可以为任意的数据结构。  

Recno算法: 要求每一个记录都有一个逻辑纪录号,逻辑纪录号由算法本身生成。实际上,这和关系型数据库中逻辑主键通常定义为int AUTO型是同一个概念。Recho建立在B+树算法之上,提供了一个存储有序数据的接口。记录的长度可以为定长或不定长。  

Queue算法:和Recno方式接近, 只不过记录的长度为定长。数据以定长记录方式存储在队列中,插入操作把记录插入到队列的尾部,相比之下插入速度是最快的。  

对算法的选择首先要看关键字的类型,如果为复杂类型,则只能选择B+树或HASH算法,如果关键字为逻辑记录号,则应该选择Recno或Queue算法。当工作集关键字有序时,B+树算法比较合适;如果工作集比较大且基本上关键字为随机分布时,选择HASH算法。Queue算法只能存储定长的记录,在高的并发处理情况下,Queue算法效率较高;如果是其它情况,则选择Recno算法,Recno算法把数据存储为平面文件格式。

创建句柄:
DB *dbp;     /* DB structure handle */
int ret;     /* function return value */

/* db_create()相当于C++接口中的构造函数 */
ret = db_create(&dbp, NULL, 0);    /* Initialize the structure. This
* database is not opened in an environment,
* so the environment pointer is NULL. */

打开数据库:
ret = dbp->open(dbp,     /* DB structure pointer */
NULL,     /* Transaction pointer */
"test.db",    /* On-disk file that holds the database. */
NULL,     /* Optional logical database name */
DB_BTREE,    /* Database access method */
flags,     /* Open flags */
0);     /* File mode (using defaults) */
常用的flags有DB_CREATE、DB_EXCL、DB_RDONLY、DB_TRUNCATE

删除数据库:
dbp->remove(dbp,     /* Database pointer */
"test.db",    /* Database file to remove */
NULL,     /* Database to remove. This is
         * NULL so the entire file is
* removed. */
0);     /* Flags. None used. */


更名数据库:
dbp->rename(dbp,     /* Database pointer */
"test.db",    /* Database file to rename */
NULL,     /* Database to rename. This is
* NULL so the entire file is
* renamed. */
"newname.db",    /* New database file name */
0);     /* Flags. None used. */

注:删除、更名数据库后不能打开,否则会段错误

错误处理:
printf("%s\n",db_strerror(ret));/* db_strerror() */

打开环境:
DB_ENV *myEnv;     /* Env structure handle */
DB *dbp;     /* DB structure handle */
u_int32_t db_flags;     /* database open flags */
u_int32_t env_flags;     /* env open flags */
int ret;     /* function return value */
ret = db_env_create(&myEnv, 0);    /* Create an environment object */
env_flags = DB_CREATE |     /* If the environment does not exist,
   create it. */
            DB_INIT_MPOOL;    /* Initialize the in-memory cache. */
ret = myEnv->open(myEnv,    /* DB_ENV ptr */
"/export1/testEnv",    /* env home directory */
env_flags,     /* Open flags */
0);     /* File mode (default) */
ret = db_create(&dbp, myEnv, 0);

增加记录:
DBT key, data;
float money = 122.45;
char *description = "Grocery bill.";
/* Zero out the DBTs before using them. */
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("Put failed because key %f already exists", money);
}


获取记录:
float money;
DBT key, data;
char *description;
/* Initialize the DBTs */
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为用户的缓冲区大小,要大于等于实际记录的长度 */
/* Database retrieval code goes here */
/*
* Money is set into the memory that we supplied.
*/
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();
/* Initialize our DBTs. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
/* Iterate over the database, retrieving each record in turn. */
while ((ret = cursorp->c_get(cursorp, &key, &data, DB_NEXT)) == 0) {
/* Do interesting things with the DBTs here. */
}
if (ret != DB_NOTFOUND) {
/* Error handling goes here */
}
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();
/* Iterate over the database, deleting each record in turn. */
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标志,这个做法对重复记录可能存在一些问题,因此可以使用先删除,后增加的方式来更新记录。*/

副数据库:
/* Now associate the secondary to the primary */
dbp->associate(dbp,     /* Primary database */
NULL,     /* TXN id */
sdbp,     /* Secondary database */
get_sales_rep,    /* Callback used for key creation. Not
* defined in this example. See the next
* section. */
0);     /* Flags */
回调函数:
int
get_sales_rep(DB *sdbp,     /* secondary db handle */
const DBT *pkey,    /* primary db record's key */
const DBT *pdata,    /* primary db record's data */
DBT *skey)     /* secondary db record's key */
{
return 0;
}
副数据库只能手工查找、删除记录,不能修改或增加记录,若需要修改记录,直接修改主数据库中的记录,然后通过回调函数自动完成对副数据库的修改。

数据库管理:
获取数据库统计信息: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(),默认用字典顺序比较
默认不支持重复记录,相同的记录采用覆盖掉现有的记录。



QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 沙发  发表于: 2010-06-30
Berkeley DB与Sqlite对比
嵌入式数据库无需安装,体积小巧,速度又很快,在很多场合可以替代目前流行的MySQL, SQLServer等大中型数据库。本文介绍两种嵌入式数据库产品:Berkeley DB和SQLite,并着重讨论它们与Java之间的接口。

通常我们采用各种数据库产品来实现对数据的存储、检索等功能,例如,Oracle,SQL Server,MySQL等等。这些产品除提供基本的查询,删除,添加等功能外,也提供了很多高级特性,如触发器,存储过程,数据备份恢复,全文检索功能等。但实际上,很多的应用,仅仅利用到了这些数据库产品的基本特性而已。而且在一些小型应用上,或者某些特殊场合的应用,比如桌面程序,这些数据库产品就明显有一些臃肿。在这些情况下,嵌入式数据库的优势就特别明显了。

嵌入式数据库无须独立运行的数据库引擎,它是由程序直接调用相应的API去实现对数据的存取操作。更直白的讲,嵌入式数据库是一种具备了基本数据库特性的数据文件。嵌入式数据库与其它数据库产品的区别是,前者是程序驱动式, 而后者是引擎响应式。嵌入式数据库的一个很重要的特点是它们的体积非常小,编译后的产品也不过几十K。这不但对桌面程序的数据存储方案是一个很好的选择,也使得它们可以应用到一些移动设备上。同时,很多嵌入式数据库在性能上也优于其它数据库,所以在高性能的应用上也常见嵌入式数据库的身影。

下面介绍的是两个开放源代码的嵌入式数据库,Berkeley DB和SQLite。同时侧重介绍如何应用Java连接这两种嵌入式数据库。

一. Berkeley DB

1. 简介

Berkeley DB是一款健壮的,高速的工业级嵌入式数据库产品,你可以在它的官方主页(见参考链接一)上发现很多知名的公司都采用了这款嵌入式数据库。 Berkeley DB的一个很重要的特点是就是高速存储。在高流量,高并发的情况下,Berkeley DB要比非嵌入式的数据库表现得更加出色。所以在一些技术实现上,Berkeley DB被作为大型关系数据库的中间数据缓冲层,用来快速的保存数据,可能会在适当的时刻再导入到大型数据库中,进而应用大型数据库所提供的更为高级的特性。

Berkeley DB虽然是开源的产品,但对某些条件下的商业性应用,却不是免费的,而且价格颇为昂贵。这些商业条件排除了开源的情况,不发放分布版本的情况,等等。比如,如果你的程序是开放源代码的或者仅仅应用到单一的网站上,在这种情况下,Berkeley DB是免费的。

2. 获得Java与Berkeley DB的接口

Berkeley DB目前的版本是4.1.25,自带了Java接口。下载的压缩包中包含C和Java语言的源代码和编译配置文件。在Windows平台,可以用MS Visual C++ 6.0或MS VC.Net编译。用VC6编译的操作如下介绍:在源代码的build_win32路径下打开VC的工程文件,之后在Build菜单中的Set Active Configuration选择db_java win32 release编译选项。在VC的Tools菜单Options选项中指定JNI.H等Java本地化接口编译时所需要头文件的位置。你会在JDK的 include路径下找到这些头文件, 例如加入的路径可能会是这样的:C:\jdk1.4\include和C:\jdk1.4\include\ win32。最后在Tools菜单中Options选项还要设置Javac.exe和Jar.exe的执行路径,这个设置会使VC开发环境也能调用 Java编译器,从而在VC环境下直接完成对Java接口类的编译和打包。在编译后,在release路径下的文件中找到db.jar, libdb41.dll, libdb_java41.dll,这三个文件组成了Berkeley DB的Java接口程序包。

3. 应用Java与Berkeley DB的接口

Berkeley DB并不是一个关系型的数据库。不能应用标准的SQL语句对数据库操作,对它的操作要调用专用的API实现。这些API提供了查询、插入、删除等功能。比如com.sleepycat.db.Db类代表数据库对象。Db类的put()方法完成的是插入功能,get()方法完成的是读出数据的功能。 com.sleepycat.db.Dbc是Berkeley DB的游标类,提供了遍历数据库记录的功能。

Berkeley DB每一个记录都有一个键值和对应的数据值,而键值和数据必须是类com.sleepycat.db.Dbt的对象或其子类的对象。Dbt提供了一些方法可以将byte数组或Object对象保存到Dbt的对象中去。比如,Dbt类中的set_data(byte[])或set_object (Object)方法。注意到目前Berkeley DB中的Java API命名方法并不符合Java的命名规范,比如set_data()方法应该命名为setData()方法。Berkeley DB许诺在下一个版本中会提供符合命名规范的Java API。

Berkeley DB对任何存入的数据都是直接原样存储到数据文件中去,无论其是二进制数据还是ASCII或Unicode等编码的文本。通常可以利用这一特性和Java串行化的概念方便的进行数据的存取。例如声明一个类


public class AccountInfo implements Serializable{
//帐户信息
public String loginName;
public String password;
public boolean auotLogin;
}


在这个 AccountInfo类中仅仅包含了数据项的定义。我们完全可以将这个类看作数据库的表中字段定义。可以用Berkeley DB保存AccountInfo对象的串行化二进制数据,以此来保存这个对象中的变量值。在操作中,先对Dbt的对象调用set_object (AccountInfo)方法,而后把这个Dbt对象作为一条纪录保存到表中。当然,我们也可以应用继承Dbt类的方法来完成对数据的保存。

下面这段简单代码演示如何将数据存入到数据库中,然后再用游标对象浏览全部数据。


//注意,下面的程序的忽略了对异常处理,写入数据初始化等等一些代码,请在适当修改后再编
//译运行它
Db dbFile = null;
//生成Db对象
dbFile = new Db(null, 0);
//用BTree方式打开数据库,库文件是在c:/temp下的mydata.db文件,表名是employee
//如果数据库不存在,则自动生成一个新的数据库。
dbFile.open(null,"c:\\temp\\mydata.db","employee",Db.DB_BTREE,
Db.DB_CREATE,0);
Dbt key = new Dbt();
Dbt data = new Dbt();
//向库文件中插入一条数据,如果已经存在,打印出错信息
if (dbFile.put(null,key, data, Db.DB_APPEND) == Db.DB_KEYEXIST) {
System.out.println("Key already exists.");
}
//关闭数据文件
dbFile.close(0);
//重新打开数据文件
dbFile = new Db(null, 0);
dbFile.open(null, "c:\\temp\\mydata.db", "employee", Db.DB_UNKNOWN, 
0, 0644);
// 声明一个数据库游标Dbc对象iterator
Dbc iterator = dbFile.cursor(null, 0);
// 遍历整个表
Dbt key = new Dbt();
while (iterator.get(key, data, Db.DB_NEXT) == 0)
{
System.out.println("reading");
}
//关闭游标和数据文件
iterator.close();
dbFile.close(0);


在运行Berkeley DB的程序时勿必在系统环境变量PATH中设置libdb41.dll和 libdb_java41.dll所在的路径。

4. Berkeley DB的存储模式

Berkeley DB提供了四种存储数据的模式:Btree,Hash,Queue和Recno。在打开数据库的时候要指定一种存储模式,比如上例中open()方法中的参数Db.DB_BTREE就是指定以Btree模式打开数据库。

Btree模式是以排序的二叉树的方式存储,Hash是以线性哈希表的方式存储。Queue用逻辑记录号做为键值,以定长的数据为记录值。Recno方式也以逻辑记录号做为键值,但可以保存定长或变长的记录值。这里提到的逻辑记录号有两种,可变的和固定的。可变逻辑记录号会根据数据记录的增加与删除做相应的变化。比如在数据库中共有100条记录,如果删除第80条记录,那么第81条记录的逻辑记录号会自动变成80,以此类推,第100条记录逻辑记录号会变成99。固定的逻辑记录号则无论数据库如何操作都不会有变化。Queue模式下,逻辑记录号只能是固定方式。 Recno模式则可通过配置来选择是采用那种类型的记录号作为键值。Btree模式也可以通过设置,将可变的逻辑记录号做为键值。

这几种存储模式各有优缺点,要根据具体的需求来选择。当键值不想用逻辑记录号时Btree或Hash是必须的选择。 Btree方式比较适合连续的顺序读取,比如,当键值是时间值,如果经常有从某一时间点开始连续读取后继的记录的操作,Btree是一种很好的选择。对随机的跳跃式读取,Hash模式则更为恰当。Queue和Recno都以记录号为键值,但前者适合先进先出的读取方式。Recno则通常是存取变长文本记录的理想存储模式。

5. Berkeley DB Environment的概念

Berkeley DB Environment为一组数据库同时提供参数设置。更为重要的是,如果要应用更高级的特性,必须要使用Environment功能,比如在想要对保存的数据进行加密存储时。

6. 更多特点

除了最基本的插入、查询、删除功能以外,Berkeley DB还提供了一些特性,比如Transaction,数据加密,同步加锁控制,错误日志等功能。下面的图片是Berkeley DB功能示意图。




回页首


二. SQLite

1. 简介

相信PHP的开发人员一定不会对SQLite感到陌生,因为在PHP5中已经集成了这个轻巧的内嵌式数据库产品。SQLite与Berkeley DB相比,在操作语句上更类似关系型数据库的产品。绝大多数标准的SQL92语句SQLite都能支持。

SQLite的版权允许无任何限制的应用,包括商业性的产品。在参考链接二上提供的SQLite官方主站上可以下载到编译后的SQLite程序。但推荐应用CVS工具下载最新版本的SQLite源代码。如果在*nux平台下,可直接用make编译。如果在Windows 平台,常用的有两种方法,一是应用在Windows平台下的Linux仿真程序,如MingW或Cygwin提供的make来编译。二是应用MS VC平台编译。后者设置略有麻烦,但可仿照参考链接五上提供的MS VC6工程文件的样例。应用到这个样例的时候,要注意的是由于SQLite源代码在不断更新,如果直接应用样例所提供的VC6工程文件编译会出现一些问题,读者要根据具体的情况稍微调整一下编译的设置。

2. 编译第三方Java接口

SQLite 源代码是C,而且官方网站上只提供了C和Tcl语言的接口。为了应用Java接口,要采用第三方的接口驱程,可在参考链接三中找到这个Java接口程序。这个接口提供了两种连接SQLite的方式:一是直接用JNI技术调用SQLite的C语言接口,这种方式要求开发人员要对SQLite本身的API也有一定的了解。在第二种方式中,接口程序实现了Java标准规范的JDBC接口,这样开发人员只要对JDBC有了解就可以了。

下面介绍在Windows系统MS VC6环境中编译SQLite Java接口(同时包括JNI和JDBC两个接口)的过程。如果你对C语言编译的设置很熟悉,可以跳这这段介绍。

第一步先把SQLite源代码编译成Lib静态库文件。具体的步骤可以直接应用下面参考链接中提供的MS VC6工作区文件,其中有一个编译SQLite到静态库的设置。编译成功后得到SQLite.lib文件。第二步要建立一个新的VC DLL项目,然后和上面介绍的Berkeley DB在编译Java本地化接口的设置一样,在VC的Tools菜单Options选项中指定JNI.H等JNI编译所要的头文件位置。同时还要指定 sqlite.h头文件位置,这个文件是在生成SQLite静态库的时候自动生成的,可以在SQLite.lib文件所在的工作区目录下面找到它,例如加入的路径为C:\sqlite\msvc6。然后在Project菜单的setting选项设置Link到SQLite.lib库文件,并再次在 Tools菜单中Options指定SQLite.lib的查找路径。注意有些情况下可能要设置予编译选项HAVE_SQLITE_COMPILE以便使用SQLite中VM的一些功能。编译成功后可得到Sqlite_jni.dll文件。

第三方接口库中的Java代码包含JNI接口和多个版本的JDBC接口程序,可根据你的JRE的版本选择相应的JDBC程序。编译这些Java代码的过程这里就不做叙述了。

编译后的Java类包加上前面得到的Sqlite_jni.dll文件,组成了SQLite的Java接口库,在应用Java语言调用JDBC或JNI接口时,都是通过应用Java的本地化技术调用Sqlite_jni.dll文件,完成对SQLite数据库的操作。

3. 应用JNI直接调用SQLite功能

下面这段代码演示如何应用JNI接口操作SQLite。可以看到Database类的exec()方法是执行SQL语句的关键:


Database db = new Database();
try {
//打开数据库
db.open("c:\\temp\\mydata.slt", 0666);
db.interrupt();
db.busy_timeout(1000);
db.busy_handler(null);
db.exec("create table account (name varchar(10),gale boolean)"
,result);
db.exec("insert into account values('steve','m')", result);
db.exec("select * from account",result);
//关闭数据库
db.close();
} catch (Exception e) {
e.printStackTrace();
}


4. 应用JDBC连接SQLite

用"SQLite.JDBCDriver"作为JDBC的驱动程序类名。连接JDBC的URL格式为jdbc:sqlite:/path。这里的path为指定到SQLite数据库文件的路径,例如:


jdbc:sqlite://dirA/dirB/dbfile
jdbc:sqlite://DRIVE:/dirA/dirB/dbfile
jdbc:sqlite://COMPUTERNAME/shareA/dirB/dbfile


参考下面的应用JDBC连接SQLite的例程:


//声明JDBC驱动程序
Class clz = Class.forName("SQLite.JDBCDriver");
//连接数据库
Connection conn = DriverManager.getConnection("jdbc:sqlite:/c:/temp/e2.db");
Statement stmt = conn.createStatement();
//生成person表,包含名子和年龄字段
stmt.execute("create table person (name varchar(100),age int)");
//插入数据
stmt.execute("insert into person values('steve',25)");
//用SQL语句读出数据
result = stmt.executeQuery("select * from person");
while(result.next()){
System.out.println(result.getString(1));
System.out.println(result.getInt(2));



运行程序时要在 Java.exe命令行加入选项java.library.path指定到Sqlite_jni.dll所在的路径。例如,如果 Sqlite_jni.dll放在c:\sqliteNative 路径下面,运行类com.e2one.MyClass 的命令行将会是这样:java -Djava.library.path=c:\sqliteNative com.e2one.MyClass。

5. SQLite的特点

SQLite 是无数据类型的数据库。虽然在生成表结构的时候,要声明每个域的数据类型,但SQLite并不做任何检查。开发人员要靠自己的程序控制输入与读出数据的类型是正确的。这里有一个例外,就是当主键为整型值时,如果要插入一个非整型值时会产生异常。另外,虽然在插入或读出数据的时候是不区分类型的,但在比较的时候,不同数据类型是有区别的。比如:


CREATE TABLE MyTable(a INTEGER, b TEXT);
INSERT INTO MyTable VALUES(0,0);


当执行下面的查询:


SELECT count(*) FROM MyTable WHERE a=='00';


会返回一条记录,因为字段a的类型是整型,而数字00与0是相等的。

而执行下面的查询则不会返回记录:


SELECT count(*) FROM MyTable WHERE b=='00';


因为字段b是字符类型,字符"00"与"0"是不相等的。

SQLite提供了对Transaction的支持。应用Transaction即保证了数据的完整性,也会提高运行速度,因为多条语句一起提交给数据库的速度会比一条一条的提交方式更快。

对二进制数据,SQLite不能直接保存。但可以先将二进制的数据转换成ASCII编码,然后再保存。Base64编码机制是最常见的把二进制数据转换成ASCII编码的手段。在SQLite的C语言代码encode.c中提供了Base64编码的功能。对Java而言,在参考链接六中提供的Apache的XML RPC项目中可以找到一个Base64编码的例子。

上面介绍了两个比较常见的嵌入式数据库,Berkeley DB速度极快,可靠性高,但学习起来有一定难度。SQLite则简单易用,速度也很快,又可以应用标准的JDBC连接,但它功能却照Berkeley略有逊色,比如加密功能、二进制数据的处理等。
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 板凳  发表于: 2010-06-30
Berkeley DB入门和例子
在网上看到不少介绍Berkeley DB的文章,几乎所有的中文文章都是介绍完入门就再也没了。大都是个概括。最近做这个,所以想系统的由浅入深的介绍一下。不清楚的地方可以和我讨论,或参照官方网站sleepycat上的文档。我用的是最新版本 db-4.4.16.NC.tar.gz,这个包中含有详细的英文文档。


为什么要使用Berkeley DB,它适合什么场合应用?
Berkeley DB并不适合所有的应用,因为简单,专一所以高效。
嵌入式数据库,的“嵌入”是指它内嵌在程序中,而不是说他只应用在嵌入式系统上。它的特点很适合应用于嵌入式系统上。当然在我们的pc机集群或大型服务器上,也可以灵活的配置,完成更艰巨的任务。


它适合于管理海量的,简单的数据。Google用Berkeley DB HA (High Availability) 来管理他们的帐户信息. Motorola在他的无线产品中用Berkeley DB跟踪移动单元。hp,microsoft,Sun Microsystems...等也都是它的大客户。它不能完全取代关系数据库,但在某些方面,它却有他们望尘莫及的高效性。


性能测试,在如下的配置上:
Linux – SuSE Linux 9.1 running on an AMD Athlon 64 processor 3200+ at 1GHz system with 1GB of RAM。
每秒钟,单条记录读操作 1,002,200次。单条记录写操作 766,034次。 用bulk APIs能进行读操作 13,501,800次。当然这些都是发生在内存中的操作,因为bdb使用了cache。 性能测试具体数据可以参考官方网站的Performance Metrics & Benchmarks: Berkeley DB。 


Berkeley DB简介
Berkeley DB可以说是一个专为程序员准备的数据库。我的文章中只针对c程序员介绍的。它还支持C++、Java、Perl、Tcl、Python和PHP等。原理和接口都差不多。它的安装很简单。


cd build_unix
../dist/configure
make
make install


这几步就ok了,其实也就是把头文件和编译好的db库放到指特定位置。甚至可以不用make install,直接在编译你的程序时用-I -L -ldb 指定头文件和连接库的位置。可以完全把它当作一个函数库来用。由db库透明的来完成对数据的管理。无论是系统中的多个进程,或者是相同进程中的多个线程,都可以在同一时间调用访问数据库的函数。而底层的数据加锁、事务日志和存储管理等都在Berkeley DB函数库中实现。他不像传统的数据库那样有client和server,还专门跑几个进程。所以应用程序不需要事先同数据库服务建立起网络连接,而是通过内嵌在程序中的Berkeley DB函数库来完成对数据的保存、查询、修改和删除等操作。 


Berkeley DB函数库本身虽然只有300KB左右,但却能够用来管理多达256TB的数据,并且在许多方面的性能还能够同商业级的数据库系统相抗衡。就拿对数据的并发操作来说,Berkeley DB能够很轻松地应付几千个用户同时访问同一个数据库的情况。因而,在资源受限的嵌入式系统上进行数据库管理,Berkeley DB也是一个不错的选择。


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


基本概念
关键字/数据 是Berkeley DB用来进行数据库管理的基础。每个 Key/Data 对构成一条记录。而整个数据库实际上就是由许多这样的结构单元所构成的。通过使用这种方式,开发人员在使用Berkeley DB提供的API来访问数据库时,只需提供关键字就能够访问到相应的数据。当然也可以也可以提供 Key 和部分Data来查询符合条件的相近数据。


一个例子来完成入门
使用过rdb的人相信都能看的懂下面的例子。简要的说一下下面持续完成的功能。作为一个简单的例子environment部分可以不是必要的,我把它的用法也一起加了进来。创建一个environment指明要把数据库文件创建到哪个目录下面。创建数据库,打开数据库,写一个记录进去,然后读出记录,然后将写入的记录删除,然后关闭environment和数据库。会了这些基本操作,你就可以使用bdb完成简单的应用了。


....................................................................


#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//only this head should include for use bdb.
#include <db.h>   
#define DATABASE "yangjian.db"


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,"/home/yangbin1/yangjian/my/db/testevn",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;
}


QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 地板  发表于: 2010-06-30
Berkeley DB的访问方法
Hash or Btree?


Hash 和 Btree方法应该被用于当逻辑记录号不是用来做主键对数据访问的情况。(如果逻辑记录号是一个secondary key,用来对数据进行访问,Btree方法是一个可能的选择,因为它支持通过一个键和一个记录号来同时的访问。)


Btrees中的键是按一定的秩序来存放的。Btrees应该被用于那些keys存在某种关系的时候。例如用时间做keys,当现在访问8AM时间戳的时候,可能下一个就访问9AM时间戳。也就是在排列顺序中附近的(near)。再比如,用names做keys,我们也许要访问那些有相同last name的,Btrees仍然是一个不错的选择。


在小的数据设置上,Hash 和 Btree在性能表现上没什么差别。在那儿,所有的,或大部分数据设置被放在了cache里面。


尽管如此,当一个一数据设置足够大的时候,会有一些重要的数据页再也装不进cache里了。这种情况下,我们上面讨论的btree在性能表现上就很重要了。
例如,因为在hash中没有排列顺序中附近的机制。所以,cache在Btree中通常比Hash中更有效。Btree方法将产生更少的I/O调用。


尽管如此,当一个数据设置更大的时候,hash访问方法能赢过btree方法。原因是btree比hash数据库包含了更多的元数据页。
数据设置可以变的非常大,以至于元数据开始支配整个cache。如果这种事情发生,Btree将不得不对每次请求都进行一次I/O操作。Cache中几乎没有地方再放置那些真正的数据页了,失去了cache的意义。而因为hash有很少的元数据,可以它的cache照样可以用来放置那些数据页,起到cahche的作用。


当一个数据更更大的时候,以至于每个随机请求,hash和btree几乎都要进行一次I/O操作的时候。在这中情况下,实际上hash只要遍历少树几个内部页(internal pages)就差不多能找到,所以这也是hash在性能上的一个优势。


应用程序对数据的访问式样也极大的影响这些行为。例如,延着光标往下遍历的话,每次I/O操作到cache中的数据,将满足接下来的很多数据请求。


如果数据设置只是比cache大一点,我们还是条件使用Btree,如果你实在有太大的数据设置,hash也许会更好一些。db_stat公用程序是一个有用的工具,用来监视,你的cache表现的怎么样。


总结:
其实到这你应该能看出来,btree是在数据不是很大的时候是很优秀的,在更大的时候,由于元数据占用太多cache的原因,导致性能下降,落后与hash了,而不是说hash能超过它。所以能在元数据占用cache不是太多以前,也就是你的cache足够大,使用btree只最好的选择。当然,如果每次访问的数据都是随机的没有什么次序,也不是near的,那用btree也没什么优势了。


针对我们的应用我只讨论了 Hash or Btree?。Queue or Recno?我就不再讨论了。






选择一个页的大小:
太大了会产生很多不必要的i/o,而且影响并发性,因为Btree, Hash and Recn都是对页上锁。太小了会使用溢出页,大量使用溢出页会严重影响性能。所以一般
页的大小都选择和文件系统的I/O块,大小相等。






选择一个cache大小:
要设置的足够大,至少能满足一次操作的数据。如果你的cache设的太小,每个新页将要强迫换出least-recently-used page。
Berkeley DB将要重新读一次树的root page对于每次数据库请求。当然cache也不是越大越好,当cache大小增长到一个特定的点时,再增加就不会对性能有什么提高了。当到达这个点时,两件事情发生了。Cache足够大以至于,几乎所有的请求都不用再访问磁盘了就能从cache中得到信息。或则是你的应用程序做一些确实很随机的访问,因此再增加cache对于下一个请求也不会有什么性能上的提高了。第二种情况发生的概率很小,因为几乎所有的应用,都显示了一些,请求的相关联性。
如果cache设定的超过了操作系统的能力,将会使用交换分区,频繁换入换出,会很影响性能。




Berkeley DB -- Access Method Wrapup






觉得有必要先把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;


1. 数据对齐
Berkeley DB没有为以DBT为参数的,返回的data/key对,或回调函数的字节对齐提供任何保证。
应用程序有责任对齐任何需要对齐的。DB_DBT_MALLOC, DB_DBT_REALLOC 和 DB_DBT_USERMEM标志可能被用来对齐存储在内存中的返回项。


2. 在bulk中取回数据
当从数据库中取回大量记录的时候,那些方法调用经常影响性能。Berkeley DB提供bulk取数据接口,它能有效的提高一些应用持续的性能要使用bulk,必须先为DB->get或DBcursor->c_get指定一个buffer。这个在c api中的实现是通过设置DBT结构的data和ulen域还有flag域被设为DB_DBT_USERMEM来引用应用程序的buffer。DB_MULTIPLE或DB_MULTIPLE_KEY 需要指定给DB->get或 DBcursor->c_get方法, 以使多条记录被返回到指定的buffer中。这两个标志的区别请看手册。
下面函数只看红色标出部分就可以了。示范如何使用bulk。
...................................................................................
int rec_display(DB *dbp)
{
DBC *dbcp;
DBT key, data;
size_t retklen, retdlen;
char *retkey, *retdata;
int ret, t_ret;
void *p;
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
/* Review the database in 5MB chunks. */
#define BUFFER_LENGTH (5 * 1024 * 1024)
if ((data.data = malloc(BUFFER_LENGTH)) == NULL)
return (errno);
data.ulen = BUFFER_LENGTH;
data.flags = DB_DBT_USERMEM;
/* Acquire a cursor for the database. */
if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) {
dbp->err(dbp, ret, "DB->cursor");
free(data.data);
return (ret);
}
for (;;) {
/*
  * Acquire the next set of key/data pairs. This code does
  * not handle single key/data pairs that won't fit in a
  * BUFFER_LENGTH size buffer, instead returning DB_BUFFER_SMALL
  * to our caller.
  */
if ((ret = dbcp->c_get(dbcp,
    &key, &data, DB_MULTIPLE_KEY | DB_NEXT)) != 0) {
  if (ret != DB_NOTFOUND)
  dbp->err(dbp, ret, "DBcursor->c_get");
  break;
}
for (DB_MULTIPLE_INIT(p, &data);;) {
  DB_MULTIPLE_KEY_NEXT(p,
    &data, retkey, retklen, retdata, retdlen);
  if (p == NULL)
  break;
  printf("key: %.*s, data: %.*s\n",
    (int)retklen, retkey, (int)retdlen, retdata);
}
}
if ((t_ret = dbcp->c_close(dbcp)) != 0) {
dbp->err(dbp, ret, "DBcursor->close");
if (ret == 0)
  ret = t_ret;
}
free(data.data);
return (ret);
}
................................................................................................




3. 记录的部分的存储和取回
在Berkeley DB的访问方法中,可以只存储或取回数据项的某一部分。这个通过设置DBT结构的DB_DBT_PARTIAL 标志来实现。
同时还要设置DBT的其他几个值。
doff 数据开始处
dlen 数据长度
例如,如果数据项是ABCDEFGHIJKL, doff的值为3是指从字节D开始。dlen为4,是指随后的4个字节DEFG。
取回记录:
当从一个数据库中取回一个数据项时,从doff位置开始的dlen字节,被返回。如果被指定的那些字节不存在,其他存在的字节将被返回。
存储记录:
下面的例子初始化数据项字节长度都是20: ABCDEFGHIJ0123456789
1,
size = 20
doff = 0
dlen = 20
data = abcdefghijabcdefghij
Result: The 20 bytes at offset 0 are replaced by the 20 bytes of data;
that is, the entire record is replaced.
ABCDEFGHIJ0123456789 -> abcdefghijabcdefghij
2,
size = 10
doff = 2
dlen = 15
data = abcdefghij
Result: The 15 bytes at offset 2 are replaced by the 10 bytes of data.
ABCDEFGHIJ0123456789 -> ABabcdefghij789
2,
size = 10
doff = 25
dlen = 0
data = abcdefghij
Result: The 0 bytes at offset 25 are replaced by the 10 bytes of data;
that is, 10 bytes are inserted into the record past the end of the
current data (\0 represents a nul byte).
ABCDEFGHIJ0123456789 -> ABCDEFGHIJ0123456789\0\0\0\0\0abcdefghij
其实就是字符串替换,把数据库中某条记录的某部分替换成指定的字符串,长度可以自动根据被替换的字符串大小进行调整。可伸缩的。

QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 4楼 发表于: 2010-06-30
Berkeley DB的架构
前面几章讲了用访问方法快速的存储和取回数据。后面主要讲任何访问方法的应用,它们是线性的和可恢复的在面对系统故障时。
Berkeley DB 底层体系结构:






如上图,应用程序调用访问方法,而访问方法使用底层的共享内存cache放置最近用过的文件页面。


当应用程序需具备恢复能力的时候,它们调用的访问方法必须预先封装在事务字系统中。程序告诉bdb事务的开始和结束点。必须准备面对特殊情况下可能的失败,导致事务异常终止。
一个例子说明具有事务保护的代码的大致样子:
for (fail = 0;;) {
    /* Begin the transaction. */
    if ((ret = dbenv->txn_begin(dbenv, NULL, &tid, 0)) != 0) {
        dbenv->err(dbenv, ret, "dbenv->txn_begin");
        exit (1);
    }


    /* Store the key. */
    switch (ret = dbp->put(dbp, tid, &key, &data, 0)) {
    case 0:
        /* Success: commit the change. */
        printf("db: %s: key stored.\n", (char *)key.data);
        if ((ret = tid->commit(tid, 0)) != 0) {
              dbenv->err(dbenv, ret, "DB_TXN->commit");
              exit (1);
        }
        return (0);
    case DB_LOCK_DEADLOCK:
    default:
        /* Failure: retry the operation. */
        if ((t_ret = tid->abort(tid)) != 0) {
              dbenv->err(dbenv, t_ret, "DB_TXN->abort");
              exit (1);
        }
        if (fail++ == MAXIMUM_RETRY)
              return (ret);
        continue;
    }
}   
Berkeley DB由五个主要的子系统构成.包括: 存取管理子系统、内存池管理子系统、事务子系统、锁子系统以及日志子系统。其中存取管理子系统作为Berkeley DB数据库进程包内部核心组件,而其他子系统都存在于Berkeley DB数据库进程包的外部。每个子系统支持不同的应用级别。


1.数据存取子系统 数据存取(Access Methods)子系统为创建和访问数据库文件提供了多种支持。Berkeley DB提供了以下四种文件存储方法:哈希文件、B树、定长记录(队列)和变长记录(基于记录号的简单存储方式),应用程序可以从中选择最适合的文件组织结构。程序员创建表时可以使用任意一种结构,并且可以在同一个应用程序中对不同存储类型的文件进行混合操作。   在没有事务管理的情况下,该子系统中的模块可单独使用,为应用程序提供快速高效的数据存取服务。数据存取子系统适用于不需事务只需快速格式文件访问的应用。 


2.内存池管理子系统   内存池(Memory pool)子系统对Berkeley DB所使用的共享缓冲区进行有效的管理。它允许同时访问数据库的多个进程或者进程的多个线程共享一个高速缓存,负责将修改后的页写回文件和为新调入的页分配内存空间。它也可以独立于Berkeley DB系统之外,单独被应用程序使用,为其自己的文件和页分配内存空间。内存池管理子系统适用于需要灵活的、面向页的、缓冲的共享文件访问的应用。


3.事务子系统 事务(Transaction)子系统为Berkeley DB提供事务管理功能。它允许把一组对数据库的修改看作一个原子单位,这组操作要么全做,要么全不做。在默认的情况下,系统将提供严格的ACID事务属性,但是应用程序可以选择不使用系统所作的隔离保证。该子系统使用两段锁技术和先写日志策略来保证数据库数据的正确性和一致性。它也可以被应用程序单独使用来对其自身的数据更新进行事务保护。事务子系统适用于需要事务保证数据的修改的应用。   


4.锁子系统   锁(Locking)子系统为Berkeley DB提供锁机制,为系统提供多用户读取和单用户修改同一对象的共享控制。数据存取子系统可利用该子系统获得对页或记录的读写权限;事务子系统利用锁机制来实现多个事务的并发控制。     该子系统也可被应用程序单独采用。锁子系统适用于一个灵活的、快速的、可设置的锁管理器。   


5.日志子系统   日志(Logging)子系统采用的是先写日志的策略,用于支持事务子系统进行数据恢复,保证数据一致性。它不大可能被应用程序单独使用,只能作为事务子系统的调用模块。   以上几部分构成了整个Berkeley DB数据库系统。各部分的关系如下图所示:     






在这个模型中,应用程序直接调用的是数据存取子系统和事务管理子系统,这两个系统进而调用更下层的内存管理子系统、锁子系统和日志子系统。     由于几个子系统相对比较独立,所以应用程序在开始的时候可以指定哪些数据管理服务将被使用。可以全部使用,也可以只用其中的一部分。例如,如果一个应用程序需要支持多用户并发操作,但不需要进行事务管理,那它就可以只用锁子系统而不用事务。有些应用程序可能需要快速的、单用户、没有事务管理功能的B树存储结构,那么应用程序可以使锁子系统和事务子系统失效,这样就会减少开销。   
Programming model


它直接链接到应用程序中,与应用程序运行于同样的地址空间中。


Programmatic APIs


DB为多种编程语言提供了API接口,其中包括C、C++、Java。
值得一提的是bdb提供dbm样式的接口,以前使用unix Dbm/Ndbm的,只需要换个头文件#include <db.h>,重新编译一下,db效率将成倍的提高。当然我们也可以使用dbm样式的接口编写简单的应用程序,这种接口比较简洁。


也为脚本语言Perl、Tcl、Python和PHP提供了接口。
对apache也以module的方式提供了接口,安装后,可以在写apache api时候直接调用。除了几个函数不一样外,其他都相同。




bdb提供的公用程序:


db_archive
打印出不再使用的日志文件路径名
db_checkpoint
监视和检查数据库日志的守护进程
db_deadlock
当死锁发生时,退出锁定要求
db_dump
把数据库文件转换成db_load能认出的文本文件
db_load
从db_dump产生的文本文件中创建出数据库文件
db_printlog
把数据库日志文件转换成人能读懂的文本
db_recover
在发生错误后,把数据库恢复到一致的状态
db_stat
显示数据库环境统计
db_upgrade
把数据库文件转换成新版本的Berkley DB格式
db_verify
对数据库文件进行一致性检查
db库
许多程序中与db相关的函数都将使db库。
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 5楼 发表于: 2010-06-30
Berkeley DB的补充
腐烂数据的处理或者说数据库文件的瘦身:
当你从Btree或Hash数据库删除key/data对时,它并不把这个返回给文件系统,这使得数据重用成为可能。也就是说Btree和Hash数据库都是只增的。当你删除大量key/data对时,你可能想使数据库文件也缩减,你应该建立一个新的数据库文件,把记录从旧文件复制过去。应该是导入导出记录,而不是直接copy文件。


字节序的问题:

例如:数字254~257。在一个小数在前(little-endian)的系统上是:
254 fe 0 0 0
255 ff 0 0 0
256 0 1 0 0
257 1 1 0 0
如果你把他们当成字符串处理那么他们的排序是糟糕的:
256
257
254
255
在一个大数在前(big-endian)系统上是:
254 0 0 0 fe
255 0 0 0 ff
256 0 0 1 0
257 0 0 1 1
and so, if you treat them as strings they sort nicely. Which means, if you use steadily increasing integers as keys on a big-endian system Berkeley DB behaves well and you get compact trees, but on a little-endian system Berkeley DB produces much less compact trees. To avoid this problem, you may want to convert the keys to flat text or big-endian representations, or provide your own Btree comparison function.
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 6楼 发表于: 2010-06-30
BerkeleyDB提供的API
熬了两天,终于把这该死的环境API给翻译出来了,不知道质量如何,这里给贴出来,各位大牛给看看什么地方有错。
  本来翻译这玩意的目的是为了给自己一个对BDB环境使用的大局观,可是现在看来作用并不是很明显,这该死的玩意太多需要细细揣摩的地方了,要在工程中使用,恐怕没有更熟练的掌握更大的工夫是有点难搞的。如果看客中有个中高手,请务必拉小弟一把,万分感谢!
  好,现在正式贴上乌焦巴弓版环境API,可信度:70%,语句:勉强能懂。

概述:
  berkeleydb的环境就是对一个或几个数据库的封装,包括log file和region file。region file就是包含了内存池cache页信息的共享内存区。可移植性:由于只有数据库文件是字序独立的(byte-order independent),所以只有它可以在不同字节序的机器间互相移植。log file则只能在字序一致的机器间移植。而region file甚至只能在相同的机器间移植,还对操作系统的版本都有要求。


  所谓byte order,是CPU的架构问题,包括:littel-endian和big-endian,也就是传说中的小头大头问题。一般的CPU都是小头的,也就是说高字节在先;相反网络就是大头,低字节在先。小头的机器上0x23AB在网络上就为0xAB23,所以我们在socket通讯时,就有htonl() 一类的函数了。


  这里有一个管理bdb环境的简单方法:为将要共享环境的应用程序的环境文件创建一个单独的home路径,这个路径是用户自己定义的,bdb的应用程序根据路径名来找它。dbd常被几个不同的进程或线程共享。环境可以包含来自同一系统不同路径的资源,程序常常由于执行或其他原因为其他路径分配资源。但一般情况下,库文件、共享region以及log file将被存储在一个路径下面的不同文件夹。


  所有共享一个db环境的程序默认是相互信任的,他们可以象访问共享region一样访问彼此的数据,他们还共享着buffer空间及locks。几个包含一致性的程序必须共享着相同的环境才能同时对一个库进行操作。


常用函数:
  一、数据库环境操作函数
  1. int db_env_create(DB_ENV **dbenvp, u_int32_t flags);
  创建环境句柄。参1为bdb环境句柄的地址,包含了数据库、日志(log file)以及缓存(caching)、锁(locking)、日志(logging)、事件(transaction)子系统。句柄中包含的管理系统环境的方法同时也管理着子系统和数据库。参2在使用DB_RPCCLIENT时创建的是一个客户端(client)环境,为0时为本地的环境.

  2. int DB_ENV->open(DB_ENV *dbenv, char *db_home, u_int32_t flags, int mode);
  打开一个环境。参2为该环境的home路径,以后所有函数使用的相对路径均是以该路径为基础。指定该路径有其他几种方法:指定DB_HOME的环境变量,在该函数中使用DB_HOME;写DB_CONFIG文件;使用DB_ENV->set_data_dir系列函数为环境相关的文件指定路径,使用这种方法的时候要记得把DB_USE_ENVIRON或者DB_USE_ENVIRON_ROOT加入flags。参数mode使用方法参见linux系统中权限表示,比如:660,664等。
  参数flags可多了,使用多个标志时请按位与"|"。大致分为四类:
  一、启用各子系统的标志
  DB_INIT_CDB   
  为并发数据存储(concurrent data store)初始化锁机制。多用户读单用户写。
  DB_INIT_LOCK
  启用锁子系统。当多进程/线程同时读写数据库时,让他们互不干扰。若所有线程都只是读数据库,没必要启用。启用了锁子系统时,通常需要启用一个死锁探测器(deadlock detector),该函数为DB_ENV->lock_detect。
  DB_INIT_LOG
  启用日志子系统以恢复因程序或系统失败产生的影响。如果日志文件已经存在或被创建,以后的日志会直接添加在以前日志的后面,而不是重写。
  DB_INIT_MPOOL
  启用共享内存缓存池子系统。注意:这个标志是无论任何情况必须要用上的。
  DB_INIT_REP
  启用备份(replication)子系统。至少需要DB_INIT_TXN、DB_INIT_LOCK与之同时使用,DB_INIT_MPOOL标志就更不用说了。
  DB_INIT_TXN
  启用事件子系统。当原子多重操作很重要以及恢复的时候,该子系统需要启用。启用该子系统已经包含了DB_INIT_LOG的启用。   
  二、决定在系统初始化的时候恢复些什么的标志
  DB_RECOVER
  在打开环境之前在环境里进行一般性的恢复。由于regions将被删除并重建,应用程序的恢复需要事件子系统,所以DB_CREATE及DB_INIT_TXN标志应该同时启用。就算没有恢复的必要,设置该标志也不会产生什么不良影响。
  DB_RECOVER_FATAL
  在打开环境之前在该环境内进行灾难恢复,基于与上面相同的原因,DB_CREATE及DB_INIT_TXN需要同时启用。   
  注意:所有dbd的恢复进程必须是单线程的,也就是说,当进行恢复这个动作时,只允许单独的线程来恢复或访问该环境。bdb文档中推荐,在打开环境的时候,将标志DB_CREATE、DB_RECOVER一直置位。注意:在DB_RECOVER_FATAL或DB_RECOVER被启用,但日志文件不存在时,DB_ENV->open仍然返回0,所以在进行恢复操作之前有必要确定一下需要的日志文件是否存在。如果你需要恢复操作的更多信息,请参考 db_archive以及db_recover.   
  三、使用指定环境路径的扩展函数相关标志
  DB_USE_ENVIRON
  使用该标志后,bdb文件命名扩展函数才有用。
  DB_USE_ENVIRON_ROOT
  使用该标志后,dbd文件命名扩展函数指定的与环境相关的一些文件路径只有root权限才可以访问。
  四、一些其他的标志
  DB_CREATE
  允许bdb环境的子系统创建任何有必要创建的文件。
  DB_LOCKDOWN
  锁住共享的dbd环境文件以及映射到内存的数据库。
  DB_PRIVATE
  在堆中分配region空间,而不是在系统共享内存或文件系统的备用内存中。用了该标志的环境种所有相关数据都将在各自进程的内存区域分配空间,互斥只存在于各个线程之间。所以环境此时只允许单进程操作,但允许多线程。
  DB_REGISTER
  检查进程使用环境时是否出错。如果由于某种原因需要进行恢复操作(recovery),但DB_RECOVER等相关的标志未被置位,那么DB_ENV->open将返回DB_RUNRECOVERY的错误号。
  DB_SYSTEM_MEM
  在共享内存区中分配region空间,而不是在文件系统备用内存或者堆中分配。参考文档中Shared Memory Regions一节。
  DB_THREAD
  允许地址空间种多线程并发。


  3. int DB_ENV->close(DB_ENV *dbenv, u_int32_t flags);
  关掉环境及其所有子系统句柄,同时释放掉所有分配的内存资源。环境句柄应该在环境中其他句柄(DB, DBC, DB_TXN, DB_LOGC以及DB_MPOOLFILE)都关闭以后再关闭。关闭时将丢弃参1中包含的数据库信息,参数2一般为0。
  如果DB_ENV->open()的时候启用了进程锁(DB_INIT_LOCK),关闭环境操作并不会去释放仍然作用着的进程锁,用户可以调用 DB_ENV->lock_vec去处理;若启用了DB_INIT_MPOOL,关闭操作会将打开的内存池文件全部关闭(调用 DB_MPOOLFILE->close),但不会处理同步问题(DB_MPOOLFILE->sync);若启用了 DB_INIT_TXN,关闭操作将忽略所有未处理事件,但要注意:关闭操作进行时,如果还存在未处理事件,这本身就是一个错误;如果log游标被调用 (DB_ENV->log_cursor)且尚未关闭,你需要自己处理。
  操作成功返回0,但是就算操作不成功,该环境句柄已经无法正常访问。


  4. int DB_ENV->remove(DB_ENV *dbenv, char *db_home, u_int32_t flags);
  销毁当前不用的环境。所有与之相关的备份文件及环境区域被删除,而日志文件、数据库文件及环境路径仍然保留。若当前环境仍然在使用,需指定DB_ENV- >FORCE标志,所有已经进入该环境区域的进程将照常运行,但新加入该环境的动作将失败。一般情况下,环境将随着数据库恢复的操作被清除,该函数并没有必要。但当打开该环境时启用了DB_SYSTEM_MEM,需要调用该函数释放掉被分配的共享内存段;另外,互斥结构被分配的系统资源也需要调用该函数来释放。在多线程操作中,只允许一个线程来进行该操作。
  db_home用来指定被销毁环境的路径;若参数3为DB_USE_ENVIRON或DB_USE_ENVIRON_ROOT,说明环境路径使用了BDB File Naming来命名,参3为DB_FORCE的情况只在应用程序无法关闭或者出现死锁的时候出现。


  5. int DB_ENV->dbremove(DB_ENV *dbenv, DB_TXN *txnid, const char *file, const char *database, u_int32_t flags);
  若数据库没有指定,删除该环境下与file相关的所有子系统的信息以及该文件里所有数据库内容;若指定,则只删除与指定file内该database相关的子系统信息及该库内容。在打开的环境句柄中的数据库不能简单的用DB->remove来删除,因为此时环境对数据库具有写保护作用。
  如果该操作是某指定应用程序事件的一部分,参数2为事件句柄,否则为NULL。参数flag可以为0或者DB_AUTO_COMMIT,后者使该操作由事件子系统来处理,这样就使得该操作具有了可恢复性。当flag为DB_AUTO_COMMIT,而参数2为NULL时,事件系统对该操作写保护,操作无效。

  6. int DB_ENV->dbrename(DB_ENV *dbenv, DB_TXN *txnid, const char *file,
const char *database, const char *newname, u_int32_t flags);
  给某数据库重命名。应该注意的事项以及参数意义与DB_ENV->dbremove相仿。


  7. int DB_ENV->get_home(DB_ENV *dbenv, const char **homep);
  得到环境的主路径,存入homep指向的地址中,该函数可以在任何时候调用。

  8. int DB_ENV->get_open_flags(DB_ENV *dbenv, u_int32_t *flagsp);
  得到打开环境时用的标志。该函数在环境打开后使用。


  9. int DB_ENV->fileid_reset(DB_ENV *dbenv, const char *file, u_int32_t flags);
  修改文件id,该操作将允许在环境中复制数据库文件,然后替代原文件使用。你可以在程序执行的任何时候调用该函数,但要注意:所有当前正被使用的物理文件不能使用该操作。
  在数据库环境缓存中,所有的库都含有唯一标志该库的ID串。如果物理数据库文件被复制后,却在相同的环境中作为新的文件与原文件使用相同的ID串,这就产生了数据腐烂。本操作为物理文件中的所有表创建新的ID串。参数file为将被创建新ID串的物理文件名;参数flags为0或DB_ENCRYPT(表示该文件进行过加密操作)。


  10. int DB_ENV->lsn_reset(DB_ENV *dbenv, const char *file, u_int32_t flags);
  该操作修改数据库的LSN,这将允许数据库文件从一个事件数据库环境移动到另外一个,你可以在程序执行的任何时候调用该函数,但要注意:该函数不能修改使用中的数据库的LSN。
  事件数据库环境中的数据库页包含着对环境日志文件的引用(LSN--log sequence numbers),将数据库文件从一个环境复制或移动到另一个环境然后修改的操作,如果没有对LSN进行清理,将导致数据的腐烂(data corruption)。对LSN的重设应该先于复制或移动数据库的操作。当LSN仍然与老的环境相关联的时候,该函数的调用将触发一致性检查 (consistency checks)。
  参数file为LSN将被清除的文件的物理路径。参数flags可以为0或者DB_ENCRYPT。


  11. int DB_ENV->dbenv_failchk(DB_ENV *dbenv, u_int32_t flags);
  该函数检查某线程或进程是否由于在对数据库操作时退出而造成了未决锁,或者有未决事件导致事件子系统出错。该操作的调用基于DB_ENV->set_thread_id或者DB_ENV->set_isalive。
  如果调用该函数检测到确实存在死锁或者未决事件,那么该操作将释放掉该锁或将未决事件忽略,还将报告与此被释放掉的锁或忽略掉的事件相关的进程、线程号,相关信息将被打印到特定的输出渠道(DB_ENV->set_msgfile)或传给某应用程序的回调函数(DB_ENV-> set_msgcall)。
  当该操作检测到退出的线程是环境恢复需要的,将返回DB_RUNRECOVERY,此时该环境已经不适合使用。
  该函数需在DB_ENV->open被调用之后使用。参数flags一般都为0。

  其他4个统计用函数省略,到需要的时候再查手册。
二、环境配置函数
  1. int DB_ENV->set_alloc(DB_ENV *dbenv, void *(*app_malloc)(size_t),
    void *(*app_realloc)(void *, size_t), void (*app_free)(void *));
  为被程序使用的环境设置分配、释放内存函数。BDB有许多由链接库为程序分配内存空间的接口,比如DBT结构中的flags参数可以为 DB_DBT_MALLOC或DB_DBT_REALLOC,再如BDB的许多统计信息函数,如:DB->stat, DB_ENV->lock_stat, DB_ENV->log_archive, DB_ENV->log_stat, DB_ENV->memp_stat以及DB_ENV->txn_stat;另外由程序为链接库分配内存空间的情况只有一种:DB-> associate。但由于链接库的版本可能存在区别(主要是在WIN NT里),所以我们调用该函数来避免这个问题。
  你不必为所有3个回调函数参数指定相关函数。但你指定的函数将与链接库函数一起使用,基于与标准链接库接口的兼容性考虑,你指定的函数须遵从ANSI C X3.159-1989的调用规则。环境打开后,使用该函数将返回EINVAL错误。


  2. int DB_ENV->set_app_dispatch(DB_ENV *dbenv,
    int (*tx_recover)(DB_ENV *dbenv, DBT *log_rec, DB_LSN *lsn, db_recops op));
  为当事件失败(abort),恢复程序指定的日志记录的动作指定回调函数。该函数应在环境打开之前调用,否则可能由于与环境的不一致性产生数据腐烂。
  回调函数参数tx_recover的参数:log_rec为一个日志记录,lsn为日志序列号,op可以为以下几个标志之一:
  DB_TXN_BACKWARD_ROLL
  由被读回的日志来判断哪个事件被触发以及终止未完成的操作,并消除(undo)日志记录描述的操作。
  DB_TXN_FORWARD_ROLL
  重做(redo)由日志记录描述的操作。
  DB_TXN_ABORT
  当一个事件终止(abort)时读回日志,消除日志记录描述的操作。
  DB_TXN_APPLY
  日志被用到一个复制地址,重做日志记录描述的操作。与DB_TXN_FORWARD_ROLL常常表示相同操作。
  DB_TXN_PRINT
  日志记录以需要的格式被打印出来供调试用。



  3. int DB_ENV->set_cachesize(DB_ENV *dbenv, u_int32_t gbytes, u_int32_t bytes, int ncache);
  设置共享内存池空间大小,也就是缓存(cache)。该缓存的大小应该为程序一般性工作数据设置大小加上少量为异常事件准备的附加内存。注意:工作空间设置比并发内存页入口(pages accessed simultaneously)要大得多。
  默认的cache大小为256K,设置时至少要为20K。所有小于500M的缓存将自动增加1/4来计量内存池过载的情况,所有大于500M的缓存则根据其指定的大小。32位系统中最大缓存为4G,64位系统中最大则为10T。参数ncache如果为0或1,cache被分配在内存中连续的空间中,若大于 1,cache则被分配为ncache块相同大小的内存。数据库环境缓存大小也可以在环境的DB_CONFIG文件中配置。语法为:
  set_cachesize   多少G   缓存附加空间多少位   缓存的块数
  例如:在DB_CONFIG文件中写下以下一句将创建被分为3块的总共500M的cache空间:
  set_cachesize   0   524288000   3
  该函数应该在环境打开之前调用,如果环境已经打开,该函数将被忽略。


  4. int DB_ENV->get_cachesize(DB_ENV *dbenv, u_int32_t *gbytesp, u_int32_t *bytesp, int *ncachep);
  得到环境cache的信息。该函数在程序运行的任何时候都可以调用。


  5. int DB_ENV->set_data_dir(DB_ENV *dbenv, const char *dir);
  设置环境中数据库文件路径。DB->open调用的时候指定的路径将以该路径为基础。数据库路径将以第一个被指定的路径为准。如果该函数没有被调用,数据库路径就是环境的home目录。
  同样,该路径的指定也可以在DB_COFIG中指定,格式为:
  set_data_dir   /usr/local/dbpath
  该函数的调用应该在环境被打开之前,且需在DB_ENV->open中指定DB_USE_ENVIRON标志,set_data_dir函数指定的路径才起作用。


  6. int DB_ENV->get_data_dirs(DB_ENV *dbenv, const char ***dirpp);
  该函数返回路径数组,该数组最后一个成员不为NULL。该操作可在程序运行的任何时间调用,成功则返回0,失败返回非0值。

  7. int DB_ENV->set_encrypt(DB_ENV *dbenv, const char *passwd, u_int32_t flags);
  为BDB的库函数指定用来进行加密解密的口令(password)。该函数并非只是对指定的环境句柄操作进行配置,事实上它配置了整个数据库的环境。该操作应该在环境打开之前调用,如果调用时环境已经打开且与该环境没有保持同步,将返回一个错误。标志为0或DB_ENCRYPT_AES,后者表示使用 Rijndael/AES算法进行加密解密。


  8. int DB_ENV->get_encrypt_flags(DB_ENV *dbenv, u_int32_t *flagsp);
  得到加密标志,可以在程序运行的任何时间调用,成功返回0并将标志存储在flagsp指向的地址中,失败则返回非0值。


  9. int DB_ENV->set_feedback(DB_ENV *dbenv, void (*db_feedback_fcn)(DB_ENV *dbenv, int opcode, int percent));
  BDB的库函数的一些操作可能占用大量的时间,应用程序调用该函数来管理这些操作的进展。当一个操作可能占用很长时间时,BDB将调用该操作指定的回调函数来处理进程信息。该函数可以在程序运行的任意时间指定。成功返回0,失败返回非0值。
  回调函数参数:
  dbenv   某未关闭的环境句柄的引用。
  opcode   为一操作码,可以是DB_RECOVER,表示环境被恢复。
  percent   表示该操作已经完成了多少,可以为0到100。


  10. int DB_ENV->set_flags(DB_ENV *dbenv, u_int32_t flags, int onoff);
  配置数据库环境,该函数的作用也可以用DB_CONFIG文件来表现,为保持同步性,除非特别说明,这些标志要么用该函数设置,要么写入DB_CONFIG文件。参数onoff为0,取消标志;为1,设置标志。
  参数flags可以为以下几个标志的中的几个,用"|"来连接。
  DB_AUTO_COMMIT   
  该标志一旦被设置,事件句柄没有指定且修改了环境中数据库的操作将被自动与事件挂钩。该标志可以在程序运行任何时期用set_flags导入环境配置。
  DB_CDB_ALLDB     
  该标志一旦被设置,BDB将针对整个数据库环境的并发存储操作上锁,而不是环境中单独的数据库。该标志可以在环境打开之前用set_flags导入环境配置。
  DB_DIRECT_DB
  关闭数据库文件的系统缓冲(buffering)以避免重复储藏(caching)。该标志可以在程序运行任何时期用set_flags导入环境配置。
  DB_DIRECT_LOG
  关闭数据库文件的的日志文件以避免重复储藏(caching)。该标志可以在程序运行任何时期用set_flags导入环境配置。
  DB_DSYNC_DB
  配置BDB,使之在从写系统调用返回之前刷新数据库写操作到备份磁盘,而不是明确的在单独的系统调用中刷新数据库写操作。该标志只在某些系统中有效(比如支持POSIX标准的O_DYNC标志的系统,支持Win32的FILE_FLAG_WRITE_THROUGH标志的系统),并且可能导致文件修改时间及其他一些文件级的信息不真确。在大多数系统中,该标志还导致运行效率的下降。所以,该标志只在有强壮的容错功能的文件系统中适用(比如Veritas VxFS文件系统)。该标志可以在程序运行任何时期用set_flags导入环境配置。
  DB_DSYNC_LOG
  配置BDB,使之在从写系统调用返回之前刷新日志写操作到备份磁盘,而不是明确的在单独的系统调用中刷新数据库日志写操作。该标志只在某些系统中有效(比如支持POSIX标准的O_DYNC标志的系统,支持Win32的FILE_FLAG_WRITE_THROUGH标志的系统),并且可能导致文件修改时间及其他一些文件级的信息不真确。该标志在有的系统导致运行效率的下降,有的却导致性能上升。该标志可以在程序运行任何时期用set_flags导入环境配置。
  DB_LOG_AUTOREMOVE
  该标志自动删除不再需要的日志文件,不过将导致灾难恢复(catastrophic recovery)变得不可能。用set_flags设置该标志将影响到对数据库环境进行操作的所有线程。该标志可以在程序运行任何时期用set_flags导入环境配置。
  DB_LOG_INMEMORY
  该标志使事件日志保留在内存而不是磁盘。这意味着事件支持ACI(atomicity,consistency,isolation)特性,而非D (durability),也就是说数据库的完整性(integrity)将被维护,但当程序或系统出错,该完整性不被保留,在这种情况下,所有的数据库文件必须从可靠的档案备份或replication group master进行恢复。用set_flags设置该标志将影响到对数据库环境进行操作的所有线程。该标志可以在环境打开之前导入环境配置。
  当为日志分配的内存满的时候,BDB将返回一个附加错误号:DB_LOG_BUFFER_FULL。所以为内存日志配置大小时,程序应该确定其足够大,以避免某事件霸占整个日志的内存以及由于某事件一直在日志内存中活跃而导致该空间没有可释放的空间的情况。
  DB_NOLOCKING
  该标志允许所有互斥锁解除而不考虑他们的现实作用。注意:只能用于调试!该标志可以在程序运行任何时期用set_flags导入环境配置,只对指定的环境起句柄作用。
  DB_NOMMAP
  该标志将把只读的数据库拷贝到本地缓存中,而不是映射(mapping)到进程内存中。该标志可以在程序运行任何时期用set_flags导入环境配置,只对指定的环境句柄起作用。
  DB_NOPANIC
  该标志将忽略掉数据库环境中所有的惊恐状态(panic state).注意:只能用于调试!该标志可以在程序运行任何时期用set_flags导入环境配置,只对指定的环境句柄起作用。
  DB_OVERWRITE
  在删除之前重写(overwrite)以加密格式存储的文件。BDB交替使用0xFF,0x00以及0xFF字节的模式来重写文件。要使文件重写有效,文件必须存储在fixed-block文件系统中。带日志的系统及日志文件系统需要对BDB源文件进行修改。该标志可以在程序运行任何时期用 set_flags导入环境配置,只对指定的环境句柄起作用。
  DB_PANIC_ENVIRONMENT
  该标志将系统环境设置为恐慌状态,该状态下的环境会拒绝任何调用BDB函数的操作,并返回DB_RUNRECOVERY。该标志不能在DB_CONFIG 文件中配置,且会在环境打开之后起作用,但可以在程序运行的任何时期导入环境配置。对整个数据库环境,包括对其进行操作的线程起作用。
  DB_REGION_INIT
  该标志一旦设置,BDB将在创建或加入一个环境时,page-fault的共享区域到内存中;另外,BDB将在创建一个环境时写共享区域,强迫虚拟内存及文件系统举例说明必要的内存及磁盘空间;也避免了以后out-of-disk space错误。该标志可以在程序运行任何时期用set_flags导入环境配置。
  DB_TIME_NOTGRANTED
  该标志使基于时钟或事件超时值的数据库调用超时返回DB_LOCK_NOTGRANTED,以区别真正的死锁返回的DB_LOCK_DEADLOCK。该操作只对指定的环境句柄起作用。
  DB_TXN_NOSYNC
  BDB将不在事件触发(commit)日志或同步刷新日志。这意味着事件支持ACI(atomicity,consistency,isolation) 特性,而非D(durability),也就是说数据库的完整性(integrity)将被维护,但当程序或系统出错,一些最常触发的事件可能在恢复操作中被取消(undone)。风险事件的数目由多少日志更新与日志内存匹配,操作系统将脏文件刷新到磁盘,以及日志被检查的频率决定。该标志可以在程序运行任何时期用set_flags导入环境配置,只对指定的环境句柄起作用。
  DB_TXN_WRITE_NOSYNC
  BDB将在事件触发时写但不同步刷新日志。这意味着事件支持ACI(atomicity,consistency,isolation)特性,而非D (durability),也就是说数据库的完整性(integrity)将被维护,但当程序或系统出错,一些最常触发的事件可能在恢复操作中被取消 (undone)。风险事件的数目由多少日志更新与日志内存匹配,操作系统将脏文件刷新到磁盘,以及日志被检查的频率决定。该标志可以在程序运行任何时期用set_flags导入环境配置,只对指定的环境句柄起作用。
  DB_YIELDCPU
  BDB将在每个内存页或互斥体获得后立即产生处理。注意:该标志只是用来做压力测试(stress testing)。该标志可以在程序运行任何时期用set_flags导入环境配置,只对指定的环境句柄起作用。




11. int DB_ENV->get_flags(DB_ENV *dbenv, u_int32_t *flagsp);
  该函数可在程序运行的任何时期调用,返回环境配置标志位。


  12. int DB_ENV->set_isalive(DB_ENV *dbenv, int (*is_alive)(DB_ENV *dbenv, pid_t pid, db_threadid_t tid));
  声明一个判断线程、进程是否还在运行的函数。该标志可以在程序运行任何时期用set_flags导入环境配置,只对指定的环境句柄起作用。回调函数参数:
  dbenv   环境句柄。
  pid   由DB_ENV->set_thread_id函数指定的进程ID。
  tid   由DB_ENV->set_thread_id函数指定的线程ID。

  13. int DB_ENV->set_paniccall(DB_ENV *dbenv, void (*db_panic_fcn)(DB_ENV *dbenv, int errval));
  当出现只能由BDB库关掉应用程序并进行恢复的情况,函数将返回DB_RUNRECOVERY。一般情况下,程序会简单的退出而不是把错误码返回。该函数指定当DB_RUNRECOVERY将被返回时调用的函数。该标志可以在程序运行任何时期用set_flags导入环境配置。


  14. int DB_ENV->set_rpc_server(DB_ENV *dbenv, CLIENT *client, char *host,
    long cl_timeout, long sv_timeout, u_int32_t flags);
  建立数据库环境和远程进程通讯(RPC)服务器之间的联系。该函数被调用后,后来对BDB库接口的调用可能会返回或抛出包含了DB_NOSERVER, DB_NOSERVER_ID或者DB_NOSERVER_HOME的异常。只对指定的环境句柄起作用,而并不包含对库环境的相关操作。该函数不可在环境打开之后调用。
  参数client:如果在函数中提供了客户端的路径,DBD就以之作为连接,并忽略host和cl_timeout参数。参数cl_timeout:指定客户端等待服务器的延时时间,为0时使用默认的延时时间;如果超时,将返回DB_NOSERVER。参数host指定了BDB服务器连接的host,以创建联络通道。参数flags为0。参数sv_timeout指定服务器允许客户端空闲的时间;一旦该延时达到,服务器将释放掉所有与该客户端相关的资源。此后该客户端连接服务器的尝试将返回DB_NOSERVER_ID,表示给了服务器一个无用的标志;注意:该值只是作为服务器的一个参考,服务器可以按自己的策略做出改动;如果该值为0,将使用默认的延时。


  15. int DB_ENV->set_shm_key(DB_ENV *dbenv, long shm_key);
  为在VxWorks或支持X/Open-style共享内存接口的系统内存中创建的BDB环境的共享内存区域指定一个基本段ID。提示:你在自己系统的shell下输入man 2 shmget,看是否支持。
  这个基础的段ID将在BDB共享内存区域首次被创建时被用到。当一个新的共享内存区域被创建时,新的ID号将比该ID略大。比如说,基本ID为35,第一个被创建的共享内存区域的ID号就是35,而下一个将是36到40之间的某数。BDB环境只创建一个主的共享内存区域,而每一个环境支持的子系统将有自己附加的共享内存区域(比如锁,日志,内存池及事件),每一个附加内存池缓存也将获得附加共享内存区域。如果相同段ID号已存在,将移出该已存在的区域。
  使用这种方式的好处有两个:其一,如果没有这个机制,无法得知应用程序是否使用相同的段ID号来创建不同的BDB环境。其二,就算每次使用相同的段ID号来创建环境,先前创建的段将被移出,而这些系统中的段将不会无限增长。
  同样,你也可以用DB_CONFIG来配置这个基础段ID,格式如下:
  set_shm_key   ID号
  只对指定的环境句柄起作用,而并不包含对库环境的相关操作。该函数不可在环境打开之后调用。若在环境打开之后调用该函数,可能由于不同步导致现存环境的数据腐烂。

  16. int DB_ENV->get_shm_key(DB_ENV *dbenv, long *shm_keyp);
  返回基本的段ID,该函数可以在程序运行的任何时期调用。


  17. int DB_ENV->set_thread_id(DB_ENV *dbenv, int (*thread_id)(DB_ENV *dbenv, pid_t *pid, db_threadid_t *tid));
  声明一个返回当前线程管理的特定pid/tid对的函数。只对指定的环境句柄起作用,而并不包含对库环境的相关操作,该函数可以在程序运行的任何时期调用。
  回调函数参数:
  pid 指向一个pid_t结构的供当前控制线程的进程ID返回的内存地址。
  tid 指向一个db_threadid_t结构的供当前控制线程的进程ID返回的内存地址。
  标准系统系统库供返回进程线程ID的调用非常多。但如果BDB程序是动态创建进程、线程,你得在分配特定ID的方面留点神。在大多数的线程系统中,进程\ 线程ID在该进程\线程退出之后马上可以重用,有这么一个情况:如果进程\线程已经退出,但DB_ENV->failchk尚未调用,这时创建新的进程\线程ID,而这个ID恰好重用了之前退出的线程\进程,这样可能会导致后来调用的DB_ENV->failchk无法正确判断之前退出的线程 \进程是否正确释放了所有BDB的资源。解决这个问题有两种方法:其一,禁止新的进程\线程在调用DB_ENV->failchk之前创建;其二,新创建的进程\线程不重用之前用过的pid/tid对。注意:为DB_ENV->set_isalive指定的is_alive函数必须与为该函数指定的thread_id函数兼容。某些系统可能不认db_threadid_t结构,出现这个问题请与客服联系。
  如果没有为程序指定thread_id函数,POSIX线程将调用getpid()或者pthread_self,UI线程将调用getpid()及thr_self()函数。

  18. int DB_ENV->set_thread_id_string(DB_ENV *dbenv, char *(*thread_id_string)(DB_ENV *dbenv,
    pid_t pid, db_threadid_t tid, char *buf));
  声明一个将pid/tid对格式化到调用方提供的buffer中的函数。只对指定的环境句柄起作用,而并不包含对库环境的相关操作。该函数可在程序运行的任何时期调用。回调函数参数:
  pid   进程ID号。
  tid   线程ID号。
  buf   就是上面提到的buffer,至少有DB_THREADID_STRLEN大小的空间。
  如果没有指定thread_id_string函数,默认会这样显示:"pid/tid"。


  19. int DB_ENV->get_timeout(DB_ENV *dbenv, db_timeout_t *timeoutp, u_int32_t flag);
  为数据库环境的锁/事件指定延时。延时计数只发生在锁的线程控制块或启用了死锁探测时,所以延时的正确设置决定于是否启用了死锁探测。这个延时值在以per-lock/per-transaction为根据的环境中可能无效。
  该函数配置了整个数据库环境,而不止是环境句柄。该函数可在程序运行的任何时期调用。


  20. int DB_ENV->set_tmp_dir(DB_ENV *dbenv, const char *dir);
  为本地临时文件指定路径。临时文件的创建将以该路径为基础。临时文件可能很大,这决定与数据库的大小。
  如果没有指定临时文件路径,以下几个路径将被按顺序检测,第一个被检测到的路径将被作为默认临时文件路径。
  系统环境变量TMPDIR,TEMP,TMP,TempFolder;由GetTempPath接口函数(Windows)返回的路径; /var/tmp,/usr/tmp,/temp,/tmp。该函数只有在打开环境时指定了DB_USE_ENVIRON及 DB_USE_ENVIRON_ROOT标志才能使用。
  同样,临时文件的路径也可以在DB_CONFIG文件中指定,格式如下:
  set_tmp_dir   /tmppath
  该函数只是配置了环境句柄,而非整个数据库环境。该函数只能在环境打开之前调用,如果在环境之后调用,可能由于不一致性产生数据腐烂。


  21. int DB_ENV->set_verbose(DB_ENV *dbenv, u_int32_t which, int onoff);
  该函数可打开/关闭BDB指定的附加信息或调试信息。为了得到附加信息,需要为程序配置冗长(verbose)信息,具体请参考DB->set_errfile(未翻译)。onoff为0时,不输出附加信息;为非0时,输出附加信息。
  数据库环境信息同样也可以用DB_CONFIG文件来配置,格式如下:
  set_verbose   DB_VERB_RECOVERY
  该函数只是配置了环境句柄,而非整个数据库环境。该函数可在程序运行的任何时期调用。参数which为以下中的一个:
  DB_VERB_DEADLOCK   启用死锁探测时显示附加信息。
  DB_VERB_RECOVERY   进行恢复时显示附加信息。
  DB_VERB_REGISTER   显示DB_ENV->open与DB_REGISTER标志相关的附加信息。
  DB_VERB_REPLICATION   显示复制消息的附加信息。如需要得到调试复制程序的完整日志。你需要在编译BDB时启用--enable-diagnostic,还需调用DB_ENV->set_verbose。
  DB_VERB_WAITSFOR   启用死锁探测时显示waits-for表。


  22. int DB_ENV->get_verbose(DB_ENV *dbenv, u_int32_t which, int *onoffp);
  由which得到哪个标志被置位或取消置位。该函数可以在程序运行期间任何时候调用。


  其他6个信息函数没有翻译,具体参考文档。
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 7楼 发表于: 2010-06-30
Berkeley DB介绍
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+树数据访问算法。在1992年,BSD UNIX第4.4发行版中包含了Berkeley DB1.85版。基本上认为这是Berkeley DB的第一个正式版。在1996年中期,Sleepycat软件公司成立,提供对Berkeley DB的商业支持。在这以后,Berkeley DB得到了广泛的应用,当前最新版本是4.3.27。 
       值得注意的是DB是嵌入式数据库系统,而不是常见的关系/对象型数据库,对SQL语言不支持,也不提供数据库常见的高级功能,如存储过程,触发器等。 

Berkeley DB的体系结构

        Berkeley DB以拥有比Microsoft SQL Server和Oracle等数据库系统而言更简单的体系结构而著称。例如,它不支持网络访问—程序通过进程内的API访问数据库。 他不支持SQL或者其他的数据库查询语言,不支持表结构和数据列。 访问数据库的程序自主决定数据如何储存在记录里,Berkeley DB不对记录里的数据进行任何包装。记录和它的键都可以达到4G字节的长度。 
       尽管架构很简单,Berkeley DB却支持很多高级的数据库特性,比如ACID 数据库事务处理,细粒度锁,XA接口,热备份以及同步复制。 
      Berkeley DB包含有与某些经典Unix数据库编程库兼容的接口,包括:dbm,ndbm和hsearch。

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

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算法把数据存储为平面文件格式。

       从DB的官方站点http://www.sleepycat.com/下载最新的软件包db-4.3.27.tar.gz,解压到工作目录,进入该目录,依次执行下列三条命令即可。 
[pre]../dist/configure
make
make install
[/pre]
执行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常用函数使用范例
[pre]#include <db.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

/* DB的函数执行完成后,返回0代表成功,否则失败 */
void print_error(int ret)
{
    if(ret != 0)
        printf("ERROR: %s\n",db_strerror(ret));
}

/* 数据结构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;

    char *fruit = "apple";
    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;
    strncpy(cust.name, "javer", 9);
    strncpy(cust.address, "chengdu", 19);
    cust.age = 32;

    /* 首先创建数据库句柄 */
    ret = db_create(&dbp, NULL, 0);
    print_error(ret);
    
    /* 创建数据库标志 */
    flags = DB_CREATE;    

    /* 创建一个名为single.db的数据库,使用B+树访问算法,本段代码演示对简单数据类型的处理 */
    ret = dbp->open(dbp, NULL, "single.db", NULL, DB_BTREE, flags, 0);
    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赋值和取值时,要做必要的类型转换。 */
    printf("The number = %d\n", *(int*)(data.data));
    
    if(dbp != NULL)
            dbp->close(dbp, 0);

    ret = db_create(&dbp, NULL, 0);
    print_error(ret);

    flags = DB_CREATE;    

    /* 创建一个名为complex.db的数据库,使用HASH访问算法,本段代码演示对复杂数据结构的处理 */
    ret = dbp->open(dbp, NULL, "complex.db", NULL, DB_HASH, flags, 0);
    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);
    
    printf("c_id = %d name = %s address = %s age = %d\n",
        cust.c_id, cust.name, cust.address, cust.age);
    
    if(dbp != NULL)
            dbp->close(dbp, 0);
}

[/pre]
[编辑]

DB游标使用范例
游标是依赖于数据库句柄的,应用程序代码框架如下:
[pre]    /* 定义一个游标变量 */
    DBC * cur;
    /* 首先打开数据库,再打开游标 */
    dbp->open(dbp, ……);
    dbp->cursor(dbp, NULL, &cur, 0);
    
    /* do something with cursor */

    /* 首先关闭,在关闭数据库 */
    cur->c_close(cur);
    dbp->close(dbp, 0);
[/pre]在游标打开后,可以以多种方式遍历特定记录。
[pre]        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 */
    }
[/pre]当想查询特定关键字对应的记录,则应对关键字赋值,并把cur->c_get()函数中标志位设置为DB_SET。例如:
[pre]        key.data = "xxxxx";
    key.size =  XXX;
    While((ret = cur->c_get(cur, &key, &data, DB_SET)) == 0)
    {
        /* do something with key and data */
    }
    
[/pre]游标的作用还有很多,如查询多重记录,插入/修改/删除记录等。
[编辑]

DB环境使用范例
环境是DB数据库的包装器,提供多种高级功能。应用程序代码框架如下:
[pre]        /* 定义一个环境变量,并创建 */
    DB_ENV *dbenv;
    db_env_create(&dbenv, 0);
    
/* 在环境打开之前,可调用形式为dbenv->set_XXX()的若干函数设置环境 */
    /* 通知DB使用Rijndael加密算法(参考资料4)对数据进行处理 */
dbenv->set_encrypt(dbenv, "encrypt_string", DB_ENCRYPT_AES);
    /* 设置DB的缓存为5M */
dbenv->set_cachesize(dbenv, 0, 5 * 1024 * 1024, 0);
/* 设置DB查找数据库文件的目录 */
    dbenv->set_data_dir(dbenv, "/usr/javer/work_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);[/pre]
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 8楼 发表于: 2010-06-30
Oracle Berkeley DB最新版支持SQL
Oracle Berkeley DB将于2010年3月底发布最新版本Oracle Berkeley DB 11g release 2,具体版本号为 11.2.5.0.xx (xx代表具体的patch版本号)。
除了对原有Oracle Berkeley DB的功能进行了一定的改进和增强(比如提升了数据压缩功能、性能优化、C/C++中系统资源自动管理功能等等),本次发布的版本中最引人瞩目的变化是我们引入了一个有用的新特性——Oracle Berkeley DB SQL,简称BDBSQL。这是自Berkeley DB诞生20多年来第一次支持SQL接口。这无论是对开源社区,还是对嵌入式数据库行业来说,都将是一件喜事。在此也感谢整个Oracle Berkeley DB 研发团队的努力工作和大家的不断支持。

新的版本,新增的BDBSQL接口,值得期待。 

Oracle Berkeley DB SQL接口简介

从Oracle Berkeley DB (简称BDB)诞生以来,它一直扮演着一个嵌入式、提供API调用的、高性能、非关系型的数据库引擎的角色, 被广泛应用于存储、金融、互联网、电子商务、汽车、消费电子、航空及国防等领域。简言之,它是一个1M大小的C语言类库,提供了基于键/值对(key/value pair)形式的并发和事务操作的API给C/C++/Java/C#/PHP等编程语言调用。由于BDB灵活高效的特点,它特别适合一些大数据量的、或者任务密集型的、或者硬件资源受限而性能要求高的嵌入式、跨平台等等的应用需求。
但我们发现,在很多场合对于关系数据库和SQL的需求是大量存在的。在 “edge”(如消费电子)或者一些大型的企业应用(如ERP)中,一个即时高效、并发的,支持SQL的、本地化嵌入式数据库通常是首选。因此,从 Oracle Berkeley DB 11g release 2开始,我们在保留原有基于key/value操作的API的同时,新增加了BDBSQL接口,实现了对SQL的支持。
BDBSQL完全兼容SQLite(著名的嵌入式开源关系数据库)原有的编程接口。以往运行在SQLite上的程序和应用都可以无缝的、方便的迁移到 Oracle Berkeley DB这个更加强大的引擎。并且,Oracle Berkeley DB和SQLite还将进行长期的官方层面的合作,保证了Oracle Berkeley DB的SQL接口和SQLite保持一致,免除了用户的后顾之忧。此外,Berkeley DB SQL完美支持很多第三方的SQLite工具,如JDBC,ODBC,FireFox 3及其SQLite Manager 插件等。
就具体实现来说,在BDBSQL中,我们将原SQLite的底层存储引擎替换为Berkeley DB的数据库引擎。如下图所示:


Oracle Berkeley DB SQL 架构图



从上面的BDBSQL实现图可以看到,Oracle Berkeley DB引入了SQLite的SQL层:包括用户接口(sqlite3(), ODBC, JDBC等)和SQL语言处理层(Tokenizer、Parser及Generator),而底层引擎(虚拟机)则使用了BDB的存储引擎。从而,将原来SQLite基于数据库级别的并发提升一个级别 - 至BDB的基于页(Page)级别的并发,并可以利用BDB的更好的内存管理、数据和事务恢复功能、更多的扩展(如Berkeley DB的db_hotbackup、db_stat、db_archive等一系列命令行工具)。

Oracle Berkeley DB 11g release 2在主流平台经过了严格的、多重的测试和认证(每个平台涵盖以十万计的测试案例),测试平台包括Solaris、*nix、Windows系列(XP, 7, Mobile)、Android 2.0(及以上)、VxWorks等等。此外,我们还提供了大量工具和资料帮助用户熟悉这套最新接口。随版本一起发布的,包括SQLite到Oracle Berkeley DB数据迁移指南、JDBC/ODBC使用指南、全文检索(Full Text Search)向导、空间数据库(R*Tree)向导、Android平台上使用向导、数据库使用手册及调优向导、数据管理员手册、测试集、代码示例等完善的资料,进一步帮助用户放心地来使用我们的产品。了解更多SQLite的语法、API、命令行等帮助,还可以参考SQLite.org上的文档。
总结一下: BDBSQL接口是一个1M大小的C语言类库,是一个高效并发的嵌入式数据库。它支持in-memory cache选项,某些场合可作为内存数据库的一个替代方案。它支持C/C++/Java/PHP等语言接口和通过JDBC/ODBC等驱动程序访问。它运行于Unix/POSIX、Windows家族、VxWorks、QNX、Android等平台。和SQLite一样,它支持SQL92标准。

Berkeley DB SQL和SQLite使用上的区别
a) 对于用户和开发人员来说,这两个产品是没有区别的。它们在SQL语法、API、命令行交互、PRAGAMAs 等方面都是一致的。我认为,用户可以体验的显著区别有可能是性能和并发了 - 由于SQLite提供的是数据库级别的锁,而Berkeley DB SQL是页(Page)级别的锁,因此后者在绝大多数测试中都会快很多 (如Insert, Update, Delete, 并发操作等)。但是,由于BDBSQL提供的细粒度锁的机制,它又会带来一些额外的开销,一些极端的测试用例下会比SQLite慢上少许(但不明显)。并且对于这些极端测试的案例,我们一直在进行性能优化。
b) 对于已有的SQLite应用程序和工具而已,由于这两者在调用接口都是一致的,因而都可以无缝支持。
c) 对于DBA人员来说,除了可以继续使用SQLite原来的管理工具,您还可以使用BDB提供的db_hotbackup、db_stat、db_archive等一系列命令行工具来备份,监控,升级等。另外,您还可以联系Oracle寻求支持。

总体而言,我们有充分理由相信Oracle Berkeley DB SQL将会比SQLite更快,更稳定。同时,我们也将会提供更好的支持服务。
BDBSQL接口和BDB key/value接口的比较

我们举个例子说明这两套API在开发上的区别。
假设有一个employee的BDB数据库(注:一个BDB的数据库,即相当于关系型数据库中的一张二维表),我们定义其主键字段为:key{empId, email},定义其value包含字段:value{empName, sex, age, startDate, status}。相应的,对应于Oracle 关系型数据库的表结构为:

TABLE: employee {
empId PRIMARY KEY,
email PRIMARY KEY,
empName,
sex,
age,
startDate,
status
}
接下来,对于数据库的操作,在BDB的语法大致为:
- 要插入一条记录,employeeDatabase.put(key{1000, john.smith@foo.com}, value{'John Smith', 'male', 30, 2005-01-01, 'OK'});
- 随后,如果要更新上面那条记录:employeeDatabase.put(key{1000, john.smith@foo.com}, value{'John Smith', 'male', 60, 2005-01-01, 'Retired'});
- 如果删除该条记录,则:employeeDatabase.delete(key{1000, john.smith@foo.com}); // 此处只需要提供key即可
- 查询记录:employeeDatabase.get(key{1000, john.smith@foo.com}); // 此处只需要提供key即可
在BDBSQL中,上面的增删改操作都可以通过标准的SQL语言来进行。如更新一条记录,可写成:
update employee set age=60, status='Retired' where empId=1000 and email='john.smith@foo.com';
总结

最后,需要强调的是,BDBSQL是作为对BDB已有功能的一个补充而不是替代。它只是本次BDB发布产品的一个新特性,并且将会像Hash、Queue、集群等功能一样持续下去。它的出现在一定程度上更丰富了BDB的应用场景。用户可以根据自己的需要,选择适合的BDB接口:
    当用户需要非常高的性能,管理非关系型数据,或者以Queue、Hash等方式来组织和访问数据的时候,可以继续选择 Oracle Berkeley DB的既有key/value API。如大型企业系统中单点登录、消息队列、工作流等模块,如管理XML、声音、照片、视频等场合,如SOA中的BAM模块、业务规则引擎,如云计算或 者云存储节点上,等等。而在用户需要一个本地持久化的支持SQL的嵌入式数据库时,BDBSQL将成为优先的选择。比如,手机的通讯录、个人web站点、桌面端 的应用(如股票软件、浏览器客户端的缓存和存储)及开发工具(如IDE)、中小企业的数据库系统、企业实时系统的一些缓存模块、小型的关系型内存数据库等 等。
正式版本的Oracle Berkeley DB 11g release 2将于2010年3月底发布。Oracle Berkeley DB整个产品家族继续以开源形式发布,并对开源社区提供支持。更多关于Oracle Berkeley DB 11g release 2的新功能和特性,请关注我们的官方网站(http://www.oracle.com/database/berkeley-db/index.html)或者本博客的后续文章。
请至官方网站下载最新版本的Oracle Berkeley DB数据库系列产品,包括Oracle Berkeley DB (及SQL),Oracle Berkeley DB Java版和Oracle Berkeley DB XML数据库。

更多需求和反馈,欢迎留言或者给我发邮件:chao.huang[at]oracle.com。
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 9楼 发表于: 2010-08-12
Firebird数据库
作为一个编程爱好者,您必须记住一句话:“80%的程序要用到数据库”——当然,为什么是80%而不是81.2或79.9%?这个您就不要和我死抠了。


继续,作为一个编程爱好者,您还必须了解的一个事实是,商业数据库很贵的,比如Oracle公司Oracle、微软公司的SQL Server,或者是IBM的DB2,它们不仅售价高,而且,你买回去以后还是想用就用:有的按数据库的用户数再加钱,有的按机器的CPU个数加倍……下面有个场景:


……您的邻居是您的母校——某中学某年级的年段长,她最近很头痛,因为期终考又来了,按照规定,老师们又得对全年段5个班级200名学生的6门功课成绩进行录入备案,并加以分析处理,比如排序,及格率、优秀率计算、最好还能和去年的同期数据进行相比……这样的工作,用Access这样桌面型数据库软件处理,实在是太困难了,年年都要花费太多的时间……如果有一款软件,能让老师找来20个义务学生分头录入,后台有个强大的数据库进行实时合并,那该多好啊!


听说您会编程,这位段长特意选择一个月黑天高的夜晚……噢错了,一个月色皎洁的夜晚登门造访,手里提着你最爱吃的零食——南老师在这里以100%的信心告诉大家,如果您学完第二学堂的课程,这样的软件完全可以一边吃零食,一边敲键盘就搞定——所以您一口答应可敬的老师的要求——最近刚刚有了免费的Turbos编程工具,更是让您充满信心。


一周后软件完工!好人做到底,你热情地把这个软件客户端安装到学校里的每一台电脑,最后挑了一台配置最好的电脑准备安装后台数据库——这时校长闻风而来,紧紧的握着您的双手,一再感谢!并当场表示要从有些困难的教育经费里,拿出1000元表达一点小意思酬劳您。您推辞数次最终笑纳,然后开始正式安装数据库。校长突然怯怯地问您:“这个数据库应该不是正版的吧?如果要购买正版,要多少钱啊?”


你的声音很低,可是办公室里所有的老师和门口围着那群可爱的孩子们,都听见了:“不会太贵啦……以咱们学校的规模是……也就……20万30万……”


尴尬吗?悲哀吗?伤心吗?愤怒吗?老师们的一番诚意、您的一番努力、学生们一番期待才有了这个软件。可是,除非您决意盗版,否则,您一生所做的软件的价值,将 埋没在那些数据库大头们的巨额收费中,将永远显得那么渺小,那么的灰头垢脸。这样的事情仅发生在您的“成绩分析系统”上吗?不,它发生在那些80%要用到数据库的软件系统里。比如企业管理,我曾经看到一套摆在货架上的中小企业管理系统软件,精美的包装上写着一行小字,大意是:“本套软件报价并不包括SQL Server系统,请客户安装之时,自行想办法”——这意思是?第1,那些客户们必须懂得什么叫“SQL Server”、第2呢,依我的理解,就是还必须知道本市的盗版软件市场在哪里。


作为一个编程爱好者,您必须了解:在中国,您个人用用盗版或者并不违法,但是,如果您传播盗版,那就是在违法!当您在给学校的机器安装盗版数据库时,小心一点噢,您在犯法。


好长的情景啊……说得大家都灰心了,难道我们将永远生活在“传播盗版”只能写那20%的软件?不是呵。作为一个编程爱好者,您最后一定要很开心地知道:在这个世界上,还有很多很多 开放源代码的数据库系统,而且不要钱的。这些数据库被全世界上亿的大大小小的公司、网站、个人开发者在使用着!本课程将重点介绍的就是其中和Borland商业数据库Interbase同出一源的“免费、开源、跨平台”的数据库 ,它对C/C++、Delphi、Java、.Net都支持、它还有嵌入式版本。它叫:Firebird。您可称呼它为“火鸟/凤凰”,不过,我最经常的叫法是“FB”。



数据库为什么很贵?因为商人知道数据库很重要;数据库为什么有开源的免费产品?因为并不是只有商人知道数据库很重要。


1、下载 Firebird


如果您安装过类似Oracle这样动辄上G的数据库系统,您可能在想FireBird是不是又要占用您漫长的下载时间和巨大的磁盘空间呢?


答案:FireBird的安装程序只有2.65M,(您的U盘长牙齿吗?就把FB塞到它的牙缝里吧)。


FB的测试版本已经到了2.0版,但我们选择它的最新的稳定版本:V1.5.3.4870。


进入Fireird稳定版的下载页面:Firebird RDBMS Downloads;然后找到:






(Windows的可执行安装程序版本)


没错,我们下载的Windows的可执行安装程序版本。当然,截图只是截至今天的最新版本。



RDBMS:关系型数据库管理系统。





接下来我们需要检查您的系统是否已它安装了旧版的FB程序——不要立即就确信您自己没有安装过,毕竟使用FB作为数据库软件还是不少,说不定您就在不知不觉中安装了,所以还是先看一眼再确定。


2、停止旧版Firebird运行


如果您的系统以前安装旧版本的FB,那么在安装新版之前,我们必须先停止旧版本的FB运行。请按以下方法进行:


进入控制面板,对于Windows XP或更高版本系统,您可能得在控制面板内先找到“其它控制面板选项”:






(如果是XP风格,则进入其它控制面板选项)


然后找到Firebird的控制版图标:






(就是这只火鸟)


点击后,进行以下操作:






(准备停止旧版FB的运行)


完成上述Step1和Step2后,点OK退出。然后,您应该重启机器。


一会儿我们安装新版之后,还会到这个界面恢复配置。


如此,我们仅是停止了旧版FB的运行。如果您在后面的安装过程遇上某些困难,那么或许您可能需要回头卸载掉旧版FB。FB的卸载和其它普通程序类似,这里不多说。


3、安装 Firebird


没什么好说啦。运行刚才下载的安装程序,一路“Next”即可以。




4、配置Firebird


请参考一下第2步所提示的控制面板的位置,进入FB服务的配置页面:






(配置Firebird的服务选项)





确保按上图完成设置后,如果您看到的图标也有个红叉,那就点图标右边的“Start”按钮,以启动我们安装的FB。正常情况下,Start之后的状态为:






(Start之后)


现在“价格便宜量又足”的Firebird,就已经成功驻扎在您的系统上了。



没错,就算安装以后,Firebird也才9M多点,但是很快,随着您的使用,这只小小鸟占用的空间就会迅速膨胀——这是数据库的本质功能:“支持数据快速存储查询”所决定的。
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
描述
快速回复

您目前还是游客,请 登录注册
批量上传需要先选择文件,再选择上传