在UNIX/Linux平台上应用Berkeley DB 11gR2 SQL
2010年4月9日
最新发布的Berkeley DB 11gR2是第一个支持SQL的版本,编译过程与以往版本稍有不同。本文将介绍如何在Linux/UNIX下编译 Berkeley DB SQL 5.0.21(以下简称DBSQL),并运行DBSQL范例。
下载安装包
从官方网站下载db-5.0.21.tar.gz并解压缩获得db-5.0.21目录。
配置
cd db-5.0.21/build_unix ../dist/configure --enable-sql --enable-sql_compact
选项说明:
- --enable-sql_compact:
- 使dbsql库(libdb_sql-5.0.so)包含db库(libdb-5.0.so),这样libdb_sql-5.0.so就不依赖于db库,其中dbsql提供了SQL API,db库包含原有的BDB功能,这样应用程序不需要链接两个库,方便使用。编译得到的库与sqlite 3.6.22版本对应。方便使用。编译得到的库与sqlite 3.6.22版本对应。
其它常用配置选项:
- 只编译静态库:
- 加上"--disable-shared"。
- 编译最小库:
- 加上"--enable-smallbuild --enable-statistics"。
- 这里的--enable-statistics是必须搭配--enable-smallbuild使用的。因为默认的smallbuild不包含dbsql所需要的statistics组件。
- 常规编译获得的DBSQL动态库大小为2.2M,而最小方式编译得到的库大小为1.1M。话说回来,对于UNIX/Linux大多数应用来说,多个1M大小不是问题,而且smallbuild会采用Os编译器优化选项(尺寸优先)取代O3选项(速度优先),性能会有所下降。
编译
make dbsql
对于动态编译方式,实际库文件在build_unix/.libs目录下,文件:
.libs/libdb_sql-5.0.so (动态库文件) .libs/dbsql (交互式命令行工具)
而对于静态编译方式,库文件在build_unix/目录下,文件:
libdb_sql-5.0.a (静态库文件) dbsql (交互式命令行工具)
如果你需要使用各种BDB工具(如db_dump, db_load等),进一步执行编译指令make,你将能够得到BDB所提供的所有的库文件和工具,包括之前make dbsql得到的库,以及除了上述db_dump, db_load之外的其他所有BDB提供的工具,熟悉BDB的用户一定对此不陌生。
最后,如果你需要清除包括Makefile在内的所有编译文件,可执行make distclean。Makefile是由configure生成的,单纯的”make clean”不能清除这部分记录。
编译并运行范例
DBSQL的C API完全沿用SQLite的C API,这里是SQLite3 C API列表。sql/examples/目录下有多个参考范例。以下我们以C语言范例sql/examples/c/ex_sql_index.c为例:
make ex_sql_index ./ex_sql_index
程序做了以下几件事情:
- 1. 把表格university加载到数据库
- 2. 打印以下SQL语句的Query Plan。我们可以看到Query Plan中的VDBE指令有32条。并且有两条向前跳转的Next指令,表示出现了两次循环迭代。
SELECT rank, country, name from university WHERE region = 'Europe' ORDER BY country; - 3. 增加关于region, country的索引
- 4. 再次打印Query Plan。我们可以看到Query Plan中的VDBE指令简化到了21条。并且只有一次向前跳转的Next指令,表示只(在索引上)出现了一次循环迭代。
- 5. 删除关于region, country的索引
以下是程序输出:
================================================================================
Load data source ../sql/examples/data/university.csv into database.
Load done.
================================================================================
STEP1. Explain a query expression
SQL: EXPLAIN
SELECT rank, country, name
from university
WHERE region = 'Europe'
ORDER BY country;
0 Trace 0 0 0 00 (null)
1 OpenEphemeral 1 3 0 keyinfo(1,BINARY) 00 (null)
2 String8 0 1 0 Europe 00 (null)
3 Goto 0 29 0 00 (null)
4 OpenRead 0 3 0 5 00 (null)
5 Rewind 0 18 0 00 (null)
6 Column 0 4 2 00 (null)
7 Ne 1 17 2 collseq(BINARY) 69 (null)
8 Column 0 0 4 00 (null)
9 Column 0 3 5 00 (null)
10 Column 0 1 6 00 (null)
11 MakeRecord 4 3 2 00 (null)
12 Column 0 3 7 00 (null)
13 Sequence 1 8 0 00 (null)
14 Move 2 9 1 00 (null)
15 MakeRecord 7 3 3 00 (null)
16 IdxInsert 1 3 0 00 (null)
17 Next 0 6 0 01 (null)
18 Close 0 0 0 00 (null)
19 OpenPseudo 2 2 3 00 (null)
20 Sort 1 27 0 00 (null)
21 Column 1 2 2 00 (null)
22 Column 2 0 4 20 (null)
23 Column 2 1 5 00 (null)
24 Column 2 2 6 00 (null)
25 ResultRow 4 3 0 00 (null)
26 Next 1 21 0 00 (null)
27 Close 2 0 0 00 (null)
28 Halt 0 0 0 00 (null)
29 Transaction 0 0 0 00 (null)
30 VerifyCookie 0 5 0 00 (null)
31 TableLock 0 3 0 university 00 (null)
32 Goto 0 4 0 00 (null)
================================================================================
STEP2. Create an index on university(region, country)
SQL: CREATE INDEX university_geo
ON university (region, country);
================================================================================
STEP3. Explain the query again, we can see the difference
SQL: EXPLAIN
SELECT rank, country, name
from university
WHERE region = 'Europe'
ORDER BY country;
0 Trace 0 0 0 00 (null)
1 Noop 0 0 0 00 (null)
2 String8 0 1 0 Europe 00 (null)
3 Goto 0 18 0 00 (null)
4 OpenRead 0 3 0 5 00 (null)
5 OpenRead 2 4 0 keyinfo(2,BINARY,BINARY) 00 (null)
6 SeekGe 2 15 1 1 00 (null)
7 IdxGE 2 15 1 1 01 (null)
8 IdxRowid 2 2 0 00 (null)
9 Seek 0 2 0 00 (null)
10 Column 0 0 3 00 (null)
11 Column 2 1 4 00 (null)
12 Column 0 1 5 00 (null)
13 ResultRow 3 3 0 00 (null)
14 Next 2 7 0 00 (null)
15 Close 0 0 0 00 (null)
16 Close 2 0 0 00 (null)
17 Halt 0 0 0 00 (null)
18 Transaction 0 0 0 00 (null)
19 VerifyCookie 0 6 0 00 (null)
20 TableLock 0 3 0 university 00 (null)
21 Goto 0 4 0 00 (null)
================================================================================
STEP4. Drop the index
SQL: DROP INDEX university_geo;
DONE.
使用dbsql shell
范例运行完后,我们还可以用dbsql shell来查看/操作生成的数据库文件:
build_unix$ ./dbsql ex_sql_index.db Berkeley DB 11g Release 2, library version 11.2.5.0.21: (March 30, 2010) Enter ".help" for instructions Enter SQL statements terminated with a ";" dbsql> .tables university dbsql> select * from university; 1|Massachusetts Institute of Technology|mit.edu|us|North America|2|1|1|7 2|Harvard University|harvard.edu|us|North America|7|2|12|1 3|Stanford University|stanford.edu|us|North America|4|4|2|24 4|University of California Berkeley|berkeley.edu|us|North America|8|3|5|32 5|Cornell University|cornell.edu|us|North America|1|5|9|37 6|University of Wisconsin Madison|wisc.edu|us|North America|3|10|6|71 7|University of Minnesota|umn.edu|us|North America|6|15|7|22 8|California Institute of Technology|caltech.edu|us|North America|18|6|20|30 9|University of Illinois Urbana Champaign|uiuc.edu|us|North America|17|7|13|51 10|University of Michigan|umich.edu|us|North America|10|8|18|55 11|University of Texas Austin|utexas.edu|us|North America|12|11|8|44 12|University of Washington|washington.edu|us|North America|22|9|4|94 13|University of Chicago|uchicago.edu|us|North America|42|16|44|2 14|Carnegie Mellon University|cmu.edu|us|North America|5|24|3|93 15|University of Pennsylvania|upenn.edu|us|North America|16|14|33|26 16|Columbia University New York|columbia.edu|us|North America|19|12|21|95 17|Texas A&M University|tamu.edu|us|North America|31|31|11|17 18|University of Maryland|umd.edu|us|North America|37|22|17|52 19|University of California Los Angeles|ucla.edu|us|North America|13|17|26|110 20|Purdue University|purdue.edu|us|North America|14|34|14|46 21|Johns Hopkins University|jhu.edu|us|North America|59|27|49|3 22|University of Cambridge|cam.ac.uk|uk|Europe|26|13|70|86 23|Pennsylvania State University|psu.edu|us|North America|27|29|16|102 24|University of Tokyo|u-tokyo.ac.jp|jp|Asia|32|26|65|31 25|University of Arizona|arizona.edu|us|North America|20|36|19|134 dbsql> .quit
结束语
以上我们介绍了如何在UNIX/Linux环境下编译DBSQL并运行范例。在这一版本开发中,我们为新增SQL部分增加了十个范例,可作为快速参考:
1.ex_sql_load: create table, insert, load csv files 2.ex_sql_query: select, order by, group by, subquery, SQL function 3.ex_sql_index: index, query explain 4.ex_sql_statment: prepare, iterate results, callback function 5.ex_sql_transaction: transaction, commit, rollback 6.ex_sql_savepoint: savepoint 7.ex_sql_multi_thread: multi-thread, transaction, lock/busy handle 8.ex_sql_binding: prepare, variable binding, transaction, bulk insert 9.ex_sql_fts3: full-text search features 10.ex_sql_rtree: create, maintain, and query r-tree index
如果您有什么问题和建议,欢迎与我联系。
我希望您能够提供 如何将PHP中的SQLite部分替换为BDB的方法。
目前我有大量的应用使用PHP的SQLite API,希望能够体验到BDB带来的速度上的提升。
谢谢。
你好啊,回答您的问题前需要先向您确认两点:
1. 您所使用的接口是PDO吗?
2. 如果使用的不是PDO,那么您使用的PHP SQLite接口是SQLite2版本还是SQLite3版本?
@赵汝聪
我使用的是PDO,因为我喜欢PDO统一的API,迁移到其他数据库平台很容易。
再次感谢。
你好,抱歉回复晚了。上周末我整理了一下如何将PDO中的SQLite部分替换为BDB的步骤。这几天我会抽空补充SQLite->DBSQL的数据迁移部分,然后再发一篇Blog介绍整个过程。谢谢!
@好大的风
你好,Blog已发.请参考http://www.bdbchina.com/2010/04/使用dbsql作为php-pdo数据库引擎/.欢迎交流!
这个版本不支持bind吗?
你好!
我编译的时候没有生成1. 1.libs/libdb_sql-5.0.so (动态库文件)
2. .libs/dbsql (交互式命令行工具)
???
你好。使用BDB出现了如下问题,解决不了,询求帮助!急盼回复!急!
出现如下问题:
ERROR:20100410.db: DB_SECONDARY_BAD: Secondary index inconsistent with primary
场景描述:
版本:db-5.0.21(最新版本)
使用主库+索引库的方式。
主库一个key对应一个value(记录重复时,允许覆盖)
索引库一个key对应多个Value(记录重复时,允许覆盖)
索引库set_flags(DB_DUPSORT);
是在读/写BDB时报的错误,请问如何解决?
或者能否留下一个邮箱,我把应用的代码发给你,不多就几个类而以。谢谢了!
@zz
你好,DBSQL是支持bind的。请参考范例db-5.0.21/sql/examples/c/ex_sql_binding.c
@ufo
请问编译过程中是否出现了什么错误?请把错误信息贴出来。另外,如果是静态编译方式,库的路径不在build_unix/.libs下,而是在build_unix/目录下,请检查。
@范滨
你好,使用secondary index需要注意: 1, 要保证primary db和secondary db的一致性,2, 要保证所有的操作都是逻辑一致的。
OTN上有一篇相关链接,请参考:http://forums.oracle.com/forums/thread.jspa?threadID=1068502&messageID=4299452#4299452
ERROR:20100410.db: DB_SECONDARY_BAD: Secondary index inconsistent with primary
这个错误会偶尔出现,当出现一次后,后续数据量增大时出现频繁也随之增加,很头疼的问题,找不出问题所在,急盼回复。
1, 要保证primary db和secondary db的一致性,
答复:是一致的,primary db和secondary db的一致性应该是由回调函数来保证的吧?
2, 要保证所有的操作都是逻辑一致的
答复:逻辑很简单,逻辑上也是一致的。
3,另外你发的那个连接用不了
以下是我使用场景的详细情况!
应用很简单:
主库:采用唯一的KEY,例如:文件ID+记录行号。这个KEY是唯一的,但也有可能会update这个Key所对应的value值,所以应是可覆盖的。
数据的插入是用的主库的连接。
索引库:不唯一的KEY,例如:是主库记录中的某个字段,该字段是可重复的。
数据的读取是用的索引库的连接。
处理是以一个文件为一个处理单位
//在文件开始时
work_Begin()
//依次处理每个文件记录
//数据的读取:从索引库读取某个字段的所有记录
//数据的插入/更新:从主库中插入或更新记录。
中间会有对数据的读取/插入/更新()的操作
//文件结束后
work_Commit()
//环境设置
envFlags=DB_PRIVATE|DB_CREATE|DB_RECOVER|DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_INIT_MPOOL|DB_THREAD;
m_pDBEnv->open(strDataPath.c_str(), envFlags, 0)
//创建主库
m_pPrimaryBDB = new Db(m_pDBEnv,0);
m_pPrimaryBDB->open(pDBTxn, strDBFileName.c_str(), NULL, DB_BTREE, DB_CREATE, 0);
//创建索引库
m_pSecondBDB = new Db(m_pDBEnv,0);
m_pSecondBDB->set_flags(DB_DUPSORT);
m_pSecondBDB->open(pDBTxn, strDBFileName.c_str(), NULL, DB_BTREE, DB_CREATE, 0);
//主库和索引库绑定
m_pPrimaryBDB->associate(pDBTxn,m_pSecondBDB,callBack_getItemCardCode,0);
//回调方法,用主库的Value中的某个字段做为索引库的Key值
int callBack_getItemCardCode(Db *pDB, const Dbt *pKey, const Dbt *pData, Dbt *sKey)
{
u_int32_t nOffset = 0;
char *itemCardCode = (char *)pData->get_data() + nOffset;
(void)pKey;
if (nOffset > pData->get_size())
{
return -1;
}
sKey->set_data(itemCardCode);
sKey->set_size((u_int32_t)strlen(itemCardCode) + 1);
return (0);
}
//pGlobalDBTxn是一个全局的Txn,用于事务一致性保证
work_Begin()
{
m_pDBEnv->txn_begin(NULL,&pGlobalDBTxn,0);
}
work_Commit()
{
//提交
pGlobalDBTxn->commit(0);
pGlobalDBTxn = NULL;
//索引库同步
m_pSecondBDB->getDBConnect()->sync(0);
//主库同步
m_pPrimaryBDB->getDBConnect()->sync(0);
}
//往主库插入数据:
saveData()
{
void* vpBuff = (void *)dbData->getFileIDAndLine().c_str();
size_t stSize = dbData->getFileIDAndLine().size() + 1;
Dbt dbtKey(vpBuff, (u_int32_t)stSize);
vpBuff = dbData->getBuffer();
stSize = dbData->getBufferSize();
Dbt dbtData(vpBuff, (u_int32_t)stSize);
//主库插入/更新数据
m_pPrimaryBDB->put(pGlobalDBTxn, &dbtKey, &dbtData, 0);
}
//从索引库读取数据:
loadData()
{
Dbt dbtKey((void *)(strKey.c_str()),(u_int32_t)(strKey.length() + 1));
Dbt dbtData;
dbtData.set_ulen(m_nBufferSize);
dbtData.set_flags(DB_DBT_USERMEM);
dbtData.set_data(m_vpBuffer);
m_pSecondBDB->cursor(pGlobalDBTxn, &m_pDBCursorp, 0);
//从索引库中查找出Key的一系列记录。
int nRet = m_pDBCursorp->get(&dbtKey, &dbtData, DB_SET);
if ( !nRet )
{
do {
DBData dbDataObj(dbtData.get_data());
pList->push_back(dbDataObj);
} while (m_pDBCursorp->get(&dbtKey, &dbtData, DB_NEXT_DUP) == 0);
}
}
@范滨
你好,只要关联正确,primary db和secondary db的一致性是由 bdb保证的。但是,如果在不同的使用场景,没有正确关联两个db,这些db就会不一致。例如,打开primary db进行操作前忘记打开secondary db,导致部分写操作未更新到 secondary db上等等。
您提供的应用场景似乎并不完整(虽然有部分代码)。这个问题应该来源于应用逻辑是否正确,例如不同的线程做了什么,怎么做,是否同步操作了两个db等 等。关 于这类型问题,推荐直接到Oracle Technology Network(OTN)上提问:
http://forums.oracle.com/forums/main.jspa?categoryID=84
OTN上与你的问题相关的主题:
http://forums.oracle.com/forums/thread.jspa?threadID=1068502&messageID=4299452#4299452
@赵汝聪
很感谢你的回复。
只要关联正确,primary db和secondary db的一致性是由 bdb保证的。但是,如果在不同的使用场景,没有正确关联两个db,这些db就会不一致。例如,打开primary db进行操作前忘记打开secondary db,导致部分写操作未更新到 secondary db上等等。
答复:经过多次检查和走读,实际上场景中的primary db和secondary db是一致的,他们的打开都是在启动初次启动时,一直打开,直到程序我退出。
您提供的应用场景似乎并不完整(虽然有部分代码)。这个问题应该来源于应用逻辑是否正确,例如不同的线程做了什么,怎么做,是否同步操作了两个db等 等。
答复:程序是多线程的,但现在测试时只使用了一个线程,也就是说,可以直观的将对BDB操作理解为顺序的,过程的。应用逻辑就像我上个回复说的一样,单线程,很简单的应用:
1.打开一个文件,同时生成新的txn
2.依次处理每条记录
首先先在BDB中读取数据,如果不存在则插入BDB中,如果存在则更新BDB数据。
3.关闭文件,txn->commit(),同时sync同步secondary db和primary db
4,继续处理下一文件。。。
关 于这类型问题,推荐直接到Oracle Technology Network(OTN)上提问:
答复:谢谢你的建议,主要是E文不好,所以担心描述的有误,BDB的中文资料和中文讨论区太少了。所以才希望在这里能解决这个问题。
PS:其实我想让你帮我检查一下环境的设置,主库和索引库的设置,以及读写时的参数设置,是否导致了这个问题?
忘记说了,平台是Uinx-AIX平台
@范滨
您好,如果您已经确定了错误重现的环境,建议写一小段完整的程序来重现这一错误。如果可以成功地重现,则把这段程序和运行过程贴到OTN上,这样可以有规范的流程来处理您的问题。程序是世界通行的语言,相信别人一看就会懂的。而且,OTN上经常有咱们中国用户在上面提问,都得到了很好的反馈。
想问一下,BDB是否有获得某数据库所有key/data对个数的接口函数吗?
@llb
本文讲的是SQL接口,在SQL里面,可以用select count(*) from your_table来统计记录数。
如果你用的是C接口,请参考DB->stat()函数:http://www.oracle.com/technology/documentation/berkeley-db/db/api_reference/C/dbstat.html。
您好,我现在在使用BDB.5.0进行url去重,但在编译的时候。出现了错误,不知道怎么解决?
/usr/local/BerkeleyDB.5.0/include/db_cxx.h:60:23: error: exception.h: 没有那个文件或目录
/usr/local/BerkeleyDB.5.0/include/db_cxx.h:1246: 错误:expected class-name before ‘{’ token
这个是什么原因呢?
我把db-5.2.28移值到arm-linux下运行,出现以下错误:
./dbsql test.db
Berkeley DB 11g Release 2, library version 11.2.5.2.28: (June 10, 2011)
Enter “.help” for instructions
Enter SQL statements terminated with a “;”
dbsql> .table
/work/test/test_db/test.db: BDB0126 mmap: Invalid argument
/work/test/test_db/test.db: BDB0126 mmap: Invalid argument
/work/test/test_db/test.db: BDB0126 mmap: Invalid argument
/work/test/test_db/test.db: BDB0126 mmap: Invalid argument
Error: SQL logic error or missing database
dbsql> .database
Error: out of memory
dbsql>
在X86的LINUX下是能正常运行的,我的ARM板内存为64M,是不是内存少了?还是需要在编译是做什么选项,请告之.谢谢.
你好,我没看到你有建立数据库的动作,因此猜测这个数据库是在x86上建立的,是吗?如果是的话,因为ARM和x86大小端不一致,你可能需要用dbsql shell里面的”dump”和”read”来在ARM平台上重建数据库。
如果这一问题仍然出现,或者修正后出现了其它问题。请问在回复中告知具体ARM型号、Linux版本号(如果是uClinux则不支持MMAP)以及系统正常运行的时候系统空闲内存(通过/proc查看)。这些信息有助于我们诊断具体的系统问题。谢谢!
@赵汝聪
您好:
我在运行db-5.2.28源码里提供的例子ex_sql_multi_thread(/db-5.2.28/examples/sql/c/ex_sql_multi_thread.c)时会发现偶尔丢行。
正常运行结果应该如下:
Check existing record number of the table
SQL: SELECT count(*) FROM university;
100025
但是偶尔会丢一到两行:
Check existing record number of the table
SQL: SELECT count(*) FROM university;
100024
发生几率差不多7‰.
并且在运行过程中,有时候会出现这样的log:DB_LOCK->lock_put: Lock is no longer valid
但是却没有如程序代码中所写的那样打出log:
fprintf(stderr, “ERROR: %s. ERRCODE: %d.\n”,
sqlite3_errmsg(db), rc);
我的运行环境是:
CentOS6
$ uname -a
Linux localhost.localdomain 2.6.32-71.el6.x86_64 #1 SMP Fri May 20 03:51:51 BST 2011 x86_64 x86_64 x86_64 GNU/Linux
Berkeley DB version: db-5.2.28
编译时选项:
../dist/configure –enable-sql_compat –enable-test –with-tcl=/usr/lib64
您有邮箱吗或是QQ吗? 能否详细讨论?
谢谢!
你好,请问程序运行在什么文件系统上?欢迎来信讨论(rucong.zhao@oracle.com), 谢谢