首页 > Berkeley DB, SQL, 赵汝聪 > 在UNIX/Linux平台上应用Berkeley DB 11gR2 SQL

在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

如果您有什么问题和建议,欢迎与我联系。

分类: Berkeley DB, SQL, 赵汝聪 标签: , , , ,
  1. 2010年4月9日17:07 | #1

    我希望您能够提供 如何将PHP中的SQLite部分替换为BDB的方法。
    目前我有大量的应用使用PHP的SQLite API,希望能够体验到BDB带来的速度上的提升。
    谢谢。

  2. 赵汝聪
    2010年4月9日23:04 | #2

    你好啊,回答您的问题前需要先向您确认两点:
    1. 您所使用的接口是PDO吗?
    2. 如果使用的不是PDO,那么您使用的PHP SQLite接口是SQLite2版本还是SQLite3版本?

  3. 2010年4月12日13:47 | #3

    @赵汝聪
    我使用的是PDO,因为我喜欢PDO统一的API,迁移到其他数据库平台很容易。
    再次感谢。

  4. 赵汝聪
    2010年4月20日10:24 | #4

    你好,抱歉回复晚了。上周末我整理了一下如何将PDO中的SQLite部分替换为BDB的步骤。这几天我会抽空补充SQLite->DBSQL的数据迁移部分,然后再发一篇Blog介绍整个过程。谢谢!

  5. 赵汝聪
    2010年4月22日21:00 | #5

    @好大的风
    你好,Blog已发.请参考http://www.bdbchina.com/2010/04/使用dbsql作为php-pdo数据库引擎/.欢迎交流!

  6. zz
    2010年5月12日18:23 | #6

    这个版本不支持bind吗?

  7. ufo
    2010年5月13日15:18 | #7

    你好!
    我编译的时候没有生成1. 1.libs/libdb_sql-5.0.so (动态库文件)
    2. .libs/dbsql (交互式命令行工具)
    ???

  8. 范滨
    2010年5月17日12:24 | #8

    你好。使用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时报的错误,请问如何解决?
    或者能否留下一个邮箱,我把应用的代码发给你,不多就几个类而以。谢谢了!

  9. 赵汝聪
    2010年5月17日14:10 | #9

    @zz
    你好,DBSQL是支持bind的。请参考范例db-5.0.21/sql/examples/c/ex_sql_binding.c

  10. 赵汝聪
    2010年5月17日14:11 | #10

    @ufo
    请问编译过程中是否出现了什么错误?请把错误信息贴出来。另外,如果是静态编译方式,库的路径不在build_unix/.libs下,而是在build_unix/目录下,请检查。

  11. 赵汝聪
    2010年5月17日17:50 | #11

    @范滨
    你好,使用secondary index需要注意: 1, 要保证primary db和secondary db的一致性,2, 要保证所有的操作都是逻辑一致的。
    OTN上有一篇相关链接,请参考:http://forums.oracle.com/forums/thread.jspa?threadID=1068502&messageID=4299452#4299452

  12. 范滨
    2010年5月17日20:21 | #12

    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);
    }
    }

  13. 赵汝聪
    2010年5月18日14:41 | #13

    @范滨
    你好,只要关联正确,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

  14. 范滨
    2010年5月18日19:25 | #14

    @赵汝聪
    很感谢你的回复。
    只要关联正确,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平台

  15. 赵汝聪
    2010年5月20日16:30 | #15

    @范滨
    您好,如果您已经确定了错误重现的环境,建议写一小段完整的程序来重现这一错误。如果可以成功地重现,则把这段程序和运行过程贴到OTN上,这样可以有规范的流程来处理您的问题。程序是世界通行的语言,相信别人一看就会懂的。而且,OTN上经常有咱们中国用户在上面提问,都得到了很好的反馈。 ;-)

  16. llb
    2010年5月29日21:15 | #16

    想问一下,BDB是否有获得某数据库所有key/data对个数的接口函数吗?

  17. chaohuang
    2010年5月31日10:38 | #17

    @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。

  18. zhdrfirst
    2010年12月17日11:08 | #18

    您好,我现在在使用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
    这个是什么原因呢?

  19. terry
    2011年8月19日15:46 | #19

    我把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,是不是内存少了?还是需要在编译是做什么选项,请告之.谢谢.

  20. 赵汝聪
    2011年9月7日18:16 | #20

    你好,我没看到你有建立数据库的动作,因此猜测这个数据库是在x86上建立的,是吗?如果是的话,因为ARM和x86大小端不一致,你可能需要用dbsql shell里面的”dump”和”read”来在ARM平台上重建数据库。

    如果这一问题仍然出现,或者修正后出现了其它问题。请问在回复中告知具体ARM型号、Linux版本号(如果是uClinux则不支持MMAP)以及系统正常运行的时候系统空闲内存(通过/proc查看)。这些信息有助于我们诊断具体的系统问题。谢谢!

  21. adamsjtu
    2011年9月9日17:10 | #21

    @赵汝聪
    您好:
    我在运行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吗? 能否详细讨论?
    谢谢!

  22. 赵汝聪
    2011年9月9日19:21 | #22

    你好,请问程序运行在什么文件系统上?欢迎来信讨论(rucong.zhao@oracle.com), 谢谢

本文的评论功能被关闭了.
Դ