`
colin115
  • 浏览: 40009 次
社区版块
存档分类
最新评论

BerkeleyDB 多索引查询

    博客分类:
  • C++
阅读更多

由于性能原因,我们打算将关系型数据库转移到内存数据库中;在内存数据库产品的选型中,我们确定的候选对象有Redis和Berkeley DB;

Redis查询效率不错,并且支持丰富的数据存储结构,但不支持多索引,这样对于比较复杂的sql移植可能会造成数据膨胀;Berkeley DB只支持简单的Key/Value, 但支持多索引查询,对我们目前的应用来说,移植起来更有优势;

 

下面我们看看,如何为DB建立二级索引;

还是用例子来说明:

一张表中记录学生的信息;每个学生有个唯一的ID,这个id通常就是表的主键;

现在,我们希望通过学生的last_name来查询,这就需要建立二级索引;

注:用词约定:

* 本文提到的“数据库”是指Berkeley DB的database,相当于关系数据库的一个表。

作为SQL的常用表:

CREATE TABLE students(student_id CHAR(4) NOT NULL,lastname CHAR(15),

firstname CHAR(15), PRIMARY KEY(student_id)); CREATE INDEX lname ON students(lastname);

在Berkeley DB中,就是定义为如下结构:

struct student_record {
    char student_id[4];
    char last_name[15];
    char first_name[15];
};

void second()
{
    DB *dbp, *sdbp;
    int ret;

    /* 创建/打开第一个数据库*/
    if ((ret = db_create(&dbp, dbenv, 0)) != 0)
        handle_error(ret);
    if ((ret = dbp->open(dbp, NULL,
        "students.db", NULL, DB_BTREE, DB_CREATE, 0600)) != 0)
        handle_error(ret);
    /* 打开第二个数据库,注意,需要申明这个库支持重复记录,因为学生的last_name不是唯一的,是可能重复的*/  
    if ((ret = db_create(&sdbp, dbenv, 0)) != 0)
        handle_error(ret);
    if ((ret = sdbp->set_flags(sdbp, DB_DUP | DB_DUPSORT)) != 0)
        handle_error(ret);
    if ((ret = sdbp->open(sdbp, NULL,
        "lastname.db", NULL, DB_BTREE, DB_CREATE, 0600)) != 0)
        handle_error(ret);

    /* 将二级个库关联到第一个库上. 注:getname是提取key函数*/
    if ((ret = dbp->associate(dbp, NULL, sdbp, getname, 0)) != 0)
        handle_error(ret);
}

/*
* getname -- 从第一个库的键值对中提取第二个库的key(即 last name)
*/
int getname(DB *secondary, const DBT *pkey, const DBT *pdata, DBT *skey)
{
    /*
     * 这里第二个key是数据的简单结构,所以并不需要做其它的工作,直接返回就完事。
     *  如果第二个key是需要从复杂记录中提取出来再组建,这个用户函数可能需要做分配空间和copy数据的工作;在这种情况下,对于第二个键的DBT结构需要设置 DB_DBT_APPMALLOC 标志位;*/
    memset(skey, 0, sizeof(DBT));
    skey->data = ((struct student_record *)pdata->data)->last_name;
    skey->size = sizeof(((struct student_record *)pdata->data)->last_name);
    return (0);
}

 

插入数据

从开发者的角度来看,插入数据与第二个索引数据库无关,直接操作第一个数据库中即可:

struct student_record s;
DBT data, key;
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
memset(&s, 0, sizeof(struct student_record));
key.data = "WC42";
key.size = 4;
memcpy(&s.student_id, "WC42", sizeof(s.student_id));
memcpy(&s.last_name, "Churchill      ", sizeof(s.last_name));
memcpy(&s.first_name, "Winston        ", sizeof(s.first_name));
data.data = &s;
data.size = sizeof(s);
if ((ret = dbp->put(dbp, txn, &key, &data, 0)) != 0)
    handle_error(ret);

 

删除数据

删除数据可以通过第一个索引(student_id)来删除,也可以通过第二个索引(last_name)来删除,无论使用哪个索引删除,被删除的都是第一个库中的真实数据;

eg: 使用第一个索引删除:

BT key;
memset(&key, 0, sizeof(DBT));
key.data = "WC42";
key.size = 4;
if ((ret = dbp->del(dbp, txn, &key, 0)) != 0)
    handle_error(ret);

 

eg:使用二级个索引删除:

 

DBT skey;
memset(&skey, 0, sizeof(DBT));
skey.data = "Churchill      ";
skey.size = 15;
if ((ret = sdbp->del(sdbp, txn, &skey, 0)) != 0)
    handle_error(ret);

 

这里需要注意的是,第二个索引并非唯一性索引,所以可能对应多条数据,执行删除操作,将删除所有对应的数据;

 

查询数据

使用第一个索引查询数据,使用DB->get();

使用第二个索引查询数据,可使用DB->pget() 或者 DB->pget()

两者的区别就是,如果使用DB->pget() ,则会将查询到的数据对应的第一个索引key同时返回;(DBC->pget()也是这样)

这里给出两者的函数原型:

#include <db_cxx.h>
int Db::get(DbTxn *txnid, Dbt *key, Dbt *data, u_int32_t flags);
int Db::pget(DbTxn *txnid, Dbt *key, Dbt *pkey, Dbt *data, u_int32_t flags); 
pkey即第一索引的key;

eg:
DBT data, pkey, skey;
memset(&skey, 0, sizeof(DBT));
memset(&pkey, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
skey.data = "Churchill      ";
skey.size = 15;
if ((ret = sdbp->pget(sdbp, txn, &skey, &pkey, &data, 0)) != 0)
    handle_error(ret);

 

错误处理

在DS或CDS上更新二级索引时,可能会产生以下错误:

• 0

• DB_BUFFER_SMALL

• DB_NOTFOUND

• DB_KEYEMPTY

• DB_KEYEXIST

为了防止这些错误,在索引更新后,最好立刻删除这个二级索引,然后重建;

注意:DB_RUNRECOVERY 和 DB_PAGE_NOTFOUND属于严重级错误,一般不会发生;

如果Berkeley DB返回了这类错误,需要首先检查数据库的完整性(使用DB->verify()),确认没问题后再重建索引;

 

总结

一旦调用DB->associate() 将两个索引库关联起来,二级索引就成为第一数据库的另一个入口;

所有的更新操作都会影响与其关联的索引库;

在二级索引上,游标的操作函数都可正常使用;

需要指出的是,对于插入操作,BDB禁止通过二级索引来插入数据,因为那样的话,就没有方法为第一数据库指明主索引。应用程序,应该在第一个数据库上使用DB->put() or DBC->put()来插入数据;

可以对建立任意多个二级索引,BDB中对这方面没有限制;只要内存大小允许,以及文件描述符够用,理论上对于一个数据库可以建立任意多个二级索引;当然,索引不是越多越好,在数据更新时,索引的更新也是不小的代价;所以,设计阶段,对于索引的建立,需要精心的设计一二;

如果发现二级索引失效了,应该通过调用DB->remove()将其删除,同时,再调用一次DB->associate() 方法来生成新的索引;

如果二级索引库不再需要了,需要先关闭数据库句柄,DB->close(),再将其删除:DB->remove();

关闭主索引库句柄时,会自动关闭所以与其关联的二级索引句柄;

 

更多参考

《Reference Guide for Berkeley DB》

http://docs.oracle.com/cd/E17076_03/html/index.html

 

Posted by: 大CC | 26SEP,2013

博客:blog.me115.com

微博:新浪微博

0
3
分享到:
评论

相关推荐

    分布式图形数据库 Titan.zip

    集群很容易扩展以支持更大的数据集,Titan有一个很好的插件式性能,这个性能让它搭建在一些成熟的数据库技术上像 Apache Cassandra、Apache HBase、 Oracle BerkeleyDB。插件式索引架构可以整合 ElasticSearch ...

    SQL-Mini-Database:基于 Berkeley DB 构建的微型数据库系统,支持有用的 SQL 子集

    Index -- 创建一个索引 Show -- 打印给定关系的属性。 MDB 支持以下 DML 命令: Abort -- 回滚自上次中止或提交以来的所有更新。 关闭——关闭没有未提交更新的数据库。 未提交的更新必须在关闭之前中止或提交。 ...

    SQLite教程(一):SQLite数据库介绍

     SQLite是目前最流行的开源嵌入式数据库,和很多其他嵌入式存储引擎相比(NoSQL),如BerkeleyDB、MemBASE等,SQLite可以很好的支持关系型数据库所具备的一些基本特征,如标准SQL语法、事务、数据表和索引等。...

    自己动手写搜索引擎(罗刚著).doc

    3.1.1 BerkeleyDB介绍 27 3.1.2 抓取网页 28 3.1.3 MP3 抓取 29 3.1.4 RSS 抓取 30 3.1.5 图片抓取 33 3.1.6 垂直行业抓取 34 3.2 抓取数据库中的内容 36 3.2.1 建立数据视图 36 3.2.2 JDBC数据库连接 36 3.2.3 增量...

    Greenplum数据库架构.pdf

    外部表(external tables)/并行加载(parallel loading) 查询优化器增强 MPP Massive Parallel Processing 大规模并行处理系统 MPP与其它数据库架构 共享磁盘 例如: Informix XPS Oracle RAC DB2 pureScale DB SAN/...

    nosql 入门教程

    13.5 Berkeley DB 226 13.6 小结 228 第四部分 掌握NoSQL 第14章 选择NoSQL 230 14.1 比较NoSQL产品 230 14.1.1 可扩展性 230 14.1.2 事务完整性和一致性 233 14.1.3 数据模型 233 14.1.4 查询支持 235 ...

    使用Subversion进行版本控制(针对 Subversion 1.4)

    删除不使用的Berkeley DB日志文件 Berkeley DB 恢复 版本库数据的移植 过滤版本库历史 版本库复制 版本库备份 总结 6. 服务配置 概述 选择一个服务器配置 svnserve服务器 svnserve使用SSH通道 Apache 的 HTTP 服务器...

    elixir:Elixir 交叉参考

    它使用 Git 作为源代码文件存储,使用 Berkeley DB 作为交叉引用数据。 在内部,它索引 Git blob而不是文件树,以避免重复工作和数据。 它有一个简单的数据结构(让人想起旧的 LXR 版本)来保持查询的简单和快速。 ...

    titan1-hadoop1 part-2

    集群很容易扩展以支持更大的数据集,Titan有一个很好的插件式性能,这个性能让它搭建在一些成熟的数据库技术上像 Apache Cassandra、Apache HBase、 Oracle BerkeleyDB。插件式索引架构可以整合 ElasticSearch 和...

    titan1-hadoop1

    集群很容易扩展以支持更大的数据集,Titan有一个很好的插件式性能,这个性能让它搭建在一些成熟的数据库技术上像 Apache Cassandra、Apache HBase、 Oracle BerkeleyDB。插件式索引架构可以整合 ElasticSearch 和...

    SOPI垂直搜索引擎系统 V2.2

    使用与GOOGLE相同的数据库系统Oralce Berkeley DB嵌入式数据库,千万级数据状况下操作数据仍保持在毫秒级,与使用ORACLE或SqlServer数据库性能提升数百倍。 系统结构的合理分离有利于分布式架构,适应未来大量的...

    数据库设计工具.docx

    BDB:Berkeley DB(BDB)是一个高性能的嵌入式数据库编程库(引擎),它能够用来保留任意类型的键值对 (Key/Value Pair),而且能够为一个键保留多个数据。Berkeley DB能够支持数千的并发线程同时操作数据库,支持...

    cantor:数据抽象,存储,发现和服务系统

    康托尔 Cantor是持久的数据抽象层; 它提供查询和检索存储为键/值对,排序集,键/值... 密钥/值存储(例如BerkeleyDB或Redis)通常用于此目的。 存储和检索数据集合; 例如,某个组中的用户列表。 关系数据库或索引引

    二、大数据与分布式.pdf

    Tip:常见的 Key-Value 类型的 NoSQL 数据库有:Redis、Tokyo Cabinet Tryant、Oracle Berkeley DB 等。 2.2 基于 Cloumn Family 存储的 NoSQL 数据库 Cloumn Family 是指具有相同性质的序列属性。它的存储结构是在...

    MySQL 5.1参考手册

    7.4.4. 多列索引 7.4.5. MySQL如何使用索引 7.4.6. MyISAM键高速缓冲 7.4.7. MyISAM索引统计集合 7.4.8. MySQL如何计算打开的表 7.4.9. MySQL如何打开和关闭表 7.4.10. 在同一个数据库中创建多个表的缺陷 7.5. 优化...

    MySQL 5.1参考手册 (中文版)

    7.4.4. 多列索引 7.4.5. MySQL如何使用索引 7.4.6. MyISAM键高速缓冲 7.4.7. MyISAM索引统计集合 7.4.8. MySQL如何计算打开的表 7.4.9. MySQL如何打开和关闭表 7.4.10. 在同一个数据库中创建多个表的缺陷 7.5. 优化...

    mysql官方中文参考手册

    7.4.4. 多列索引 7.4.5. MySQL如何使用索引 7.4.6. MyISAM键高速缓冲 7.4.7. MyISAM索引统计集合 7.4.8. MySQL如何计算打开的表 7.4.9. MySQL如何打开和关闭表 7.4.10. 在同一个数据库中创建多个表的缺陷 7.5. 优化...

    MYSQL中文手册

    7.4.4. 多列索引 7.4.5. MySQL如何使用索引 7.4.6. MyISAM键高速缓冲 7.4.7. MyISAM索引统计集合 7.4.8. MySQL如何计算打开的表 7.4.9. MySQL如何打开和关闭表 7.4.10. 在同一个数据库中创建多个表的缺陷 7.5...

    MySQL 5.1参考手册中文版

    7.4.4. 多列索引 7.4.5. MySQL如何使用索引 7.4.6. MyISAM键高速缓冲 7.4.7. MyISAM索引统计集合 7.4.8. MySQL如何计算打开的表 7.4.9. MySQL如何打开和关闭表 7.4.10. 在同一个数据库中创建多个表的缺陷 7.5....

Global site tag (gtag.js) - Google Analytics