日期:2014-05-16  浏览次数:20390 次

BerkeleyDB 多索引查询

由于性能原因,我们打算将关系型数据库转移到内存数据库中;在内存数据库产品的选型中,我们确定的候选对象有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)来删除,无论使用哪个索引删除,被删除的都是第