由于性能原因,我们打算将关系型数据库转移到内存数据库中;在内存数据库产品的选型中,我们确定的候选对象有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

微博:新浪微博

BerkeleyDB 多索引查询的更多相关文章

  1. SQL Server-聚焦强制索引查询条件和Columnstore Index(九)

    前言 本节我们再来穿插讲讲索引知识,后续再讲数据类型中的日期类型,简短的内容,深入的理解,Always to review the basics. 强制索引查询条件 前面我们也讲了一点强制索引查询的知 ...

  2. mongodb 关系、引用、覆盖索引查询

    一.关系 MongoDB 的关系表示多个文档之间在逻辑上的相互联系.文档间可以通过嵌入和引用来建立联系.MongoDB 中的关系可以是:1对1,1对多,多对1,多对多. 一个用户可以用多个地址,这是典 ...

  3. MongoDB 覆盖索引查询

    MongoDB 覆盖索引查询 官方的MongoDB的文档中说明,覆盖查询是以下的查询: 所有的查询字段是索引的一部分 所有的查询返回字段在同一个索引中 由于所有出现在查询中的字段是索引的一部分, Mo ...

  4. MongoDB覆盖索引查询

    官方的MongoDB的文档中说明,覆盖查询是以下的查询: 1. 所有的查询字段是索引的一部分 2. 所有的查询返回字段在同一个索引中 由于所有出现在查询中的字段是索引的一部分, MongoDB 无需在 ...

  5. Mysql 版本号、存储引擎、索引查询

    [1]Mysql 版本号.存储引擎.索引查询 # 查看数据库版本号 SELECT VERSION(); # 查看数据库支持的引擎(默认即Support == DEFAULT行) SHOW ENGINE ...

  6. mysql 索引查询 、创建 create index 与 add index 的区别

    1.索引查询 ------TABLE_SCHEMA  库名:TABLE  表名 ------AND UPPER(INDEX_NAME) != 'PRIMARY'  只查询索引,不需要主键 SELECT ...

  7. OceanBase支持索引查询啦!

    OceanBase支持索引后,上面的例子中用索引的查询速度是不用索引查询速度的40倍! 相关阅读 OceanBase简史 OceanBase Join操作 OceanBase内部表 OceanBase ...

  8. cassandra——可以预料的查询,如果你的查询条件有一个是根据索引查询,那其它非索引非主键字段,可以通过加一个ALLOW FILTERING来过滤实现

    cassandra的索引查询和排序 转自:http://zhaoyanblog.com/archives/499.html   cassandra的索引查询和排序 cassandra的查询虽然很弱,但 ...

  9. elasticsearch索引查询,日志搜素

    索引查询 http://10.199.137.115:9200/_cat/indices?format=json 返回json字符串的索引状态 增加索引名称过滤 http://10.199.137.1 ...

随机推荐

  1. android 开发工具(android studio)

      Android Studio 从安装到配置使用 okhttp比xutils功能强大,源码地址: https://github.com/search?utf8=✓&q=okhttp andr ...

  2. arduino 蓝牙控制RGB LED灯

    /* 日期:2016.9.2 功能:arduino 蓝牙控制RGB LED灯 元件: 跳线公公头 * 8 rgbled, 220欧电阻 蓝牙模块 接线: 蓝牙模块VCC,GND分别接5V,GND;TX ...

  3. SELECT INTO和INSERT INTO SELECT

    --自动创建了target_table表,并复制source_table表的数据到target_table select name,age into target_table from source_ ...

  4. Android的init过程(二):初始化语言(init.rc)解析【转】

    转自:http://www.cnblogs.com/nokiaguy/p/3164799.html Android的init过程(一) 本文使用的软件版本 Android:4.2.2 Linux内核: ...

  5. linux 集群配置ssh无密码访问

    一.修改host文件 1) 用客户端工具(ssh client或者putty)连接到linux服务器.在root用户下输入命令 vi /etc/hosts,用vi编辑hosts文件,如下: #127. ...

  6. Binary Tree Level Order Traversal II

    /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode ...

  7. MYSQL的主从和主主复制模式

    一.复制介绍 MySQL支持单向.异步复制,复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器.主服务器将更新写入二进制日志文件,并维护文件的一个索引以跟踪日志循环.这些日志可以记录 ...

  8. 【转】Tomcat调优指南

    转载地址:http://blog.csdn.net/woohooli/article/details/3954792 1          概述 本文档主要介绍了Tomcat的性能调优的原理和方法.可 ...

  9. android 代码整体回退

    repo forall -c 'HAHA=`git log --before="3 days" -1 --pretty=format:"%H"`;git res ...

  10. c# 之抽象工厂模式

    Email整体项目 Email类 using System; using System.Collections.Generic; using System.Linq; using System.Tex ...