10225501448 李度 10225101546 陈胤遒 10215501422 高宇菲
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

5.2 KiB

1. 项目概述

leveldb中的存储原本只支持简单的字节序列,在这个项目中我们对其功能进行拓展,使其可以包含多个字段,并通过这些字段实现类似数据库列查询的功能。但如果仅通过字段查找数据,需要对整个数据库的遍历,不够高效,因此还要新增二级索引,提高对特定字段的查询效率。

本文档涵盖的设计内容只是最初的设想,实现过程中大概率会进行调整甚至重构。第5、6、7部分也将在项目落实的过程中进行补充完善。

2. 功能设计

2.1 字段设计

设计目标:对value存储读取时进行序列化编码,使其支持字段。

实现思路:设计之初有考虑增加一些元数据(例如过滤器、字段偏移支持二分)来加速查询。但考虑到在数据库中kv的数量是十分庞大的,新加数据结构会带来巨大的空间开销。因此我们决定在这里牺牲时间换取空间,而将时间的加速放在索引中。
在这一基础上,我们对序列化进行了简单的优化:将字段名排序后,一一调用leveldb中原本的编码方法PutLengthPrefixedSlice存入value。这样不会有额外的空间开销,而好处在于遍历一个value的字段时,如果得到的字段名比目标大,就可以提前结束遍历。
最终db类提供了新接口putFields, getFields,分别对传入的字段序列化后调用原来的put, get接口。
FindKeysByField调用NewIterator遍历所有数据,field名和值符合则加入返回的key中。这里的问题在于遍历中可能符合条件的key之前已经被删了,所以还要维护一个被删的key数组?总感觉这个玩意性能有点太差了
cyq:如果考虑mvcc的话,感觉好像不用担心遍历过程中的问题

2.2 二级索引

设计目标:对某个字段或属性建立索引,提高对该字段的查询效率。需考虑索引的创建、删除、维护。

实现思路: 这一部分的难点主要在于,索引数据与原数据的存储需要进行隔离,不同操作之间存在同步与异步问题,还需要考虑两种数据间的一致性。为了使设计简洁化,避免不同模块耦合带来潜在的问题,我们的设计如下:

  1. 总体上,我们对两种数据分别创建一个db类的对象kvDb, indexDb。对外的接口类FieldDb包含了这两个对象,提供原先的leveldb各种接口,以及新功能,并在这一层完成两个对象的管理。
  2. kvDb与原先的存储一致,indexDb中key是原key与索引字段的联合编码,value是空(详见数据结构设计),并且lab1中新增的接口也不会被调用到。
  3. 在open新的fieldDb时,会创建这两个对象并open他们。fieldDb会维护一个字符串数组fieldWithIndex,记录了当前哪些字段名拥有索引。一个当前状态成员pair<indexStatus, fieldName>类型的nowTask,表示数据库当前处于 空闲free、创建索引中creating、删除索引中deleting,及对应的字段名(free时为空)。一个pair<indexStatus, fieldName>的队列taskQueue, 维护了创建或者删除索引的任务请求。创建或删除索引时,把任务加入队尾并休眠。一个任务完成后唤醒队首继续下一个任务(类似write机制)
class FieldDb {
    Db *kvDb;
    Db *indexDb;
    string[] fieldWithIndex;
    pair<indexStatus, fieldName> nowTask;
    queue<pair<indexStatus, fieldName>> taskQueue;
    vector<key, fieldVal> tmpAddKvs;
    vector<key, fieldVal> tmpDeleteKvs;

    //原db所有对外接口,下面没提就调用kvDb相应函数
}
  1. 一个创建/删除索引任务开始时,先修改nowTask,再对kvDb调用FindKvsByField(类似lab1的FindKeysByField但也要返回field对应的值),对返回的数组(合并上5中提到的tmpAddKvs、tmpDeleteKvs)中每个元素一一与字段名编码成新key,调用indexDb中的put或delete。
  2. fieldDb也提供了put, get,getFields, putFields,delete接口。对前三者,简单调用kvDb中的对应接口(不涉及索引)。对putFields,先调用kvDb中的putFields,写完后如果写入的字段名中:
    nowTask.fieldName,则加入tmpAddkvs,保证创建删除索引时,新来的数据也被记录。
    fieldWithIndex中有的字段,则直接对kvDb调用一个(多个)put
    delete也是如此,创建删除索引时新来的要delete的数据由tmpDeletekvs维护。
    只考虑到这,理论上每个数据库内部的日志、快照、合并等模块不会互相影响(创建的那些current、manifest文件名好像会冲突) cyq:这个问题比较好解决,其实只要把两个数据库放到两个不同的子目录里面就可以了。 但是这里有个问题是,先putfields后,index加入tmpAddkvs前有对于索引的读取,那么会不会有不一致的问题

3. 数据结构设计

indexDb的kv编码:暂时考虑助教文档那种

4. 接口/函数设计

5. 功能测试

6. 可能的挑战与解决方案

已想到的部分在之前已阐述,其余待发现。

7. 分工与进度安排

功能 完成日期 分工
value序列化 11.19 李度