首页 > Berkeley DB JE, Chao Huang, 程序设计 > 在Berkeley DB Java版中实现SQL查询

在Berkeley DB Java版中实现SQL查询

2009年2月26日 chaohuang

前言

众所周知,Berkeley DB 产品家族(包括了 C语言版,Java版和XML数据库)是一套高效率的,可扩展的嵌入式数据库引擎。此处,所谓数据库引擎是指它提供了除SQL处理层以外的所有关系数据库的功能(如事务性(ACID)支持,数据存储,数据恢复等)。那么,您不禁要问,为什么Berkeley DB产品不提供对SQL的支持呢?答案很简单,它可以嵌入到您的应用代码中,在应用代码的地址空间运行,从而达到高效率。

到底有多高效?

在我自己的DELL OptiPlex 745 台式机上(Intel(R) Core(TM)2 CPU 6400, 2G memory, running Ubuntu 7.04 Server x86_64),用自己写的一个单线程的例子程序在9秒内读取了100万条记录,22秒内插入100万的记录。

从关系表的角度看,我插入数据的行定义,大致如下:

f0      INT PRIMARY KEY,
f1      STRING PRIMARY KEY,
id      LONG,
one     STRING,
two     DOUBLE,
three   STRING,
zip     INT,
city    STRING,
state   STRING

在Berkeley DB Java 版中的定义如下:

@Persistent
class CompositeKey {

    @KeyField(1)
    int f0 = 0;
    @KeyField(2)
    String f1 = "The quick brown fox jumps over the lazy dog.";

    CompositeKey() { } // for bindings

    CompositeKey(int f0) {
        this.f0 = f0;
    }

    CompositeKey(int f0, String f1) {
        this.f0 = f0;
        this.f1 = f1;
    }

    @Override
    public String toString() {
        return "CompositeKey: (" + f0 + "," + f1 + ")";
    }
}

@Entity
class BasicEntity {

    @PrimaryKey
    CompositeKey key;

    protected long id = 0;
    protected String one = "one";
    protected double two = .2d;
    protected String three = "three";
    Address address = new Address();

    BasicEntity() { }

    BasicEntity(int i) {
        this.key = new CompositeKey(i);
    }

    public void modify() {
        id++;
        one += "1";
        two = id;
        three += "3";
        address = new Address("Shenzhen", "Guangdong, China", 500001);
    }

    @Override
    public String toString() {
        return "BasicEntity: (" + key + "," + id + "," + one + "," +
            two + "," + three + ", " + address + ")";
    }
}

@Persistent
class Address {

    private String city = "Boston";
    private String state = "Massachusetts";
    private int zip = 10001;

    Address() { }

    Address(String city, String state, int zip) {
        this.city = city;
        this.state = state;
        this.zip = zip;
    }

    @Override
    public String toString() {
        return "Address: (" + city + "," + state + "," + zip + ")";
    }
}

如何实现SQL查询

对于现在做JAVA企业应用的,他们很多都熟悉SQL语句。于是不禁要问:我想利用Berkeley DB Java版的高效,轻量级的类库,达到和关系数据库一样的功用,如何实现?也就意味着,我要自己写SQL解析器,支持SQL查询,插入,更改和删除操作,该怎么写呢?会不会很麻烦?

我的白皮书《Performing Queries in Oracle Berkeley DB Java Edition》里详细论述了,如何利用Berkeley DB Java版的直接持久层(也就是一套基于Berkeley DB Java版的底层接口和JDK5 Annotations 的拓展,英文简称叫DPL),实现了如下的SQL查询:

  • SELECT * FROM tab ORDER BY col ASC;
  • SELECT * FROM tab WHERE col LIKE ‘prefix%’;
  • SELECT * FROM tab WHERE col >= A AND col <= B;
  • SELECT * FROM tab WHERE col1 = A AND col2 = B;
  • SELECT t1.* FROM table1 t1, table2 t2 WHERE t1.col1 = t2.col1 AND t2.col2 = A;

当然,阅读以后您会发现,在SQL查询的代码里稍做修改就变成了SQL更新/删除的操作。最后,恭喜您,现在您知道如何写一个最基本的SQL解释器了,不是吗?:-)

  1. 2009年6月17日16:16 | #1

    BDB JE 不错,已经被我在网站开发中使用了。使用了DPL。谢谢!。:-)

  2. chaohuang
    2009年6月18日10:20 | #2

    @Anderson Mao
    聪明!据我所知,目前国内能用好BDB JE的案例不多。你是怎么用的?用的怎么样?不妨简单说说,分享一下你的经验,或者一起探讨一下。

  3. 徐明松
    2009年10月10日17:18 | #3

    我看了那个书之后,讲的都是sql的查询啊,
    怎么来实现删除和修改哦???
    请指点一下。谢谢!

  4. chaohuang
    2009年10月12日12:44 | #4

    @徐明松
    DPL的curosr(如EntityCursor)除了提供get方法,还提供了delete和update方法。如果是做查询,选择get;更新和删除选择update/delete。具体请参考直接持久层的Javadoc: http://www.oracle.com/technology/documentation/berkeley-db/je/java/com/sleepycat/persist/package-frame.html。

  5. 徐明松
    2009年10月12日14:50 | #5

    谢谢……非常谢谢

  6. wangyazhen
    2009年10月21日16:58 | #6

    @chaohuang
    这个网址打不开 能给个详细连接吗

    我曾经使用bdb做过网页抽取技术的研究,算是蛮成功的,但是如果数据库除了问题,怎么去恢复它,这个问题困扰了很久

  7. chaohuang
    2009年10月21日17:09 | #7

    @wangyazhen
    点击JE的Javadoc地址。打开后,选择com.sleepycat.persist包。

    恢复有很多策略,如果你带着具体问题来问会好些。我建议你带着问题去JE的论坛提问-网址见右侧边栏的“常用链接”。

  8. 三葳
    2010年1月20日17:04 | #8

    高手们好:
    我用BDB-JE写入16W数据,然后重起电脑,在进行查询,查询100条,第一次速度为1秒多,那么我在查1W多次就能达到500毫秒吧,不知道您是怎么写的会这么快,我设置了Cache为2个G,但是我查看了每次读数据缓存命中为0,不知道是怎么回事,高手能指点一下吗谢谢了,这是我的QQ375391019和邮箱:sanzang_xulong@126.com 请那位好心高手指教一下谢谢了,我弄好一个多星期了速度就是上不好,郁闷死了!

  9. chaohuang
    2010年1月25日11:13 | #9

    @三葳
    首先,我觉得你程序测试的是硬盘IO的速率,而非JE的性能。理由是,你是电脑重起后进行查询的(cache是冷的;并且你程序preload数据大小的值很低,才10M),因而NCacheMiss很高,很多时间耗费在IO wait上。

    我们在笔记本(Windows, 5400rpm 硬盘)和台式机(Linux, 7200rpm 硬盘)上的测试结果也印证我的观点。我们建议你:
    * 首先,建议用JE的DPL,而不是base API。
    * 你可以试试在Linux下面运行你的程序(降低I/O的额外开销),如果有可能,配置7200rpm或更高的硬盘。
    * 你把JVM的大小尽可能设大一点(例如java -Xmx1024M),然后preload的大小尽可能地调高(如preload 300M),这样的目的是将nCacheMiss降到最低。

    采用如上3点建议,我们在台式机上用你程序的测试耗时约在50 ms (-Xmx1024M, 默认的cache设置, preload 所有数据, 100次随机读耗时50ms)。

  10. 2011年6月26日14:00 | #10

    文档都不能下了! 网上也找不到 想用BDB还真挺难,能不能提供点资料,发送:yubaojian0616@163.com
    谢谢!
    请问 BDB怎么实现in查询 这对于我们用的lucene+SQL in 组合很有用,怎么提高它的性能! 数据大概80G 大约 600+万条

  11. chaohuang
    2011年6月28日11:29 | #11

    @于堡舰
    OTN 更新了文档链接,新的地址为: http://www.oracle.com/technetwork/database/berkeleydb/performing.pdf

    如果实现in查询,你需要遍历所有记录,逐条判断是否符合条件。

    你考虑购买BDB的license吗?你可以给我发邮件:chao.huang [at] oracle.com。 Oracle会阻止163/263等邮件,所以推荐使用gmail/hotmail/yahoo mail。

  12. adsl
    2011年11月17日17:44 | #12

    @chaohuang 请问java版怎么实现sql查询 比如select * from table where A=? and B=? and c<? order by D 我看了好久了 没有找到答案 希望得到帮助

  13. chaohuang
    2011年12月2日14:00 | #13

    @adsl
    你可以参考我的那篇文章:
    1. 得到SELECT * FROM tab where A=? and B=?的结果(假设是一个集合或者一个游标),然后针对该集合或者游标做“c<”和”order by D”。
    2. 或者,先得到SELECT * FROM tab where c<的结果(假设是一个集合或者一个游标),然后针对该集合或者游标做“A=? and B=?”和”order by D”。

    选译#1或者#2关键看中间结果集的大小,即集合或者游标的结果集的大小。返回的中间结果集越小,后续处理越高效。

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