Преглед изворни кода

大致完成设计文档

pull/1/head
augurier пре 9 месеци
родитељ
комит
17ed0600c7
1 измењених фајлова са 45 додато и 15 уклоњено
  1. +45
    -15
      设计文档.md

+ 45
- 15
设计文档.md Прегледај датотеку

@ -1,7 +1,7 @@
# 1. 项目概述
leveldb中的存储原本只支持简单的字节序列,在这个项目中我们对其功能进行拓展,使其可以包含多个字段,并通过这些字段实现类似数据库列查询的功能。但如果仅通过字段查找数据,需要对整个数据库的遍历,不够高效,因此还要新增二级索引,提高对特定字段的查询效率。
本文档涵盖的设计内容只是最初的设想,实现过程中大概率会进行调整甚至重构。第5、6、7部分也将在项目落实的过程中进行补充完善。
本文档涵盖的设计内容只是最初的设想,实现过程中大概率会进行调整甚至重构。部分也将在项目落实的过程中进行补充完善。
# 2. 功能设计
## 2.1 字段设计
@ -18,37 +18,67 @@ leveldb中的存储原本只支持简单的字节序列,在这个项目中我
实现思路: 这一部分的难点主要在于,索引数据与原数据的存储需要进行隔离,不同操作之间存在同步与异步问题,还需要考虑两种数据间的一致性。为了使设计简洁化,避免不同模块耦合带来潜在的问题,我们的设计如下:
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机制)
3. 在open新的fieldDb时,会创建这两个对象并open他们。fieldDb会维护一个字符串数组`fieldWithIndex`,记录了当前哪些字段名拥有索引。一个`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;
queue<pair<bool indexStatus, std::string fieldName>> taskQueue;
// 0建 or 1删,或把pair抽象成一个类
//原db所有对外接口,下面没提就调用kvDb相应函数
bool CreateIndexOnField(const std::string& fieldName){、
//加入taskQueue并休眠/拿写锁
//kvdb->FindKvsByField得到数据信息
//编码
//kvdb->writeIndexLog写索引日志
//indexDb->put存储索引
//fieldWithIndex.insert(fieldName)
//唤醒taskQueue队首/释放写锁
}
bool DeleteIndex(std::string &fieldName); //同上
std::vector<std::string> QueryByIndex(Field &field){
//判断fieldWithIndex是否有
//indexDb->iterator得到编码后的数据信息
//解码返回
}
}
```
4. 一个创建/删除索引任务开始时,先修改`nowTask`,再对`kvDb`调用`FindKvsByField`(类似lab1的`FindKeysByField`但也要返回field对应的值),对返回的数组(合并上5中提到的tmpAddKvs、tmpDeleteKvs)中每个元素一一与字段名编码成新key,调用`indexDb`中的put或delete。当`indexdb`把所有操作执行完后,`FieldDb`把一个`<indexStatus, fieldName>`写入一个文件`IndexLog`,并在`fieldWithIndex`中添加/删除这个`fieldName`。**这样分离了两个数据库的写,只有indexDb全写完才能读这个索引,在这之前崩溃了就回滚不要了。在数据库恢复时,先用kvdb的日志把kvdb恢复了,再根据IndexLog的内容重新创建索引,indexdb里的恢复就不需要了。另外这个地方的测试感觉也不好做**
5. fieldDb也提供了`put`, `get`,`getFields`, `putFields`,`delete`接口。对前三者,简单调用`kvDb`中的对应接口(不涉及索引)。对`putFields`,先调用`kvDb`中的`putFields`,写完后如果写入的字段名中:
有`nowTask.fieldName`,则加入`tmpAddkvs`,保证创建删除索引时,新来的数据也被记录。
有`fieldWithIndex`中有的字段,则直接对`kvDb`调用一个(多个)`put`。
`delete`也是如此,创建删除索引时新来的要delete的数据由`tmpDeletekvs`维护。
6. 对两个数据库的其他部分,理论上每个数据库内部的其他模块不会互相影响(注意创建的那些current、manifest文件名会冲突)
4. 一个创建/删除索引任务开始时,首先锁住写锁,再对`kvDb`调用`FindKvsByField`(类似lab1的`FindKeysByField`但也要返回field对应的值),对返回的数组中每个元素一一与字段名编码成新key,**合并在一个writebatch中写入日志**,再调用`indexDb`中的put或delete。当`indexdb`把所有操作执行完后,在`fieldWithIndex`中添加/删除这个`fieldName`,使得用户可以针对这个字段进行索引读,最后唤醒下一个任务,没有则释放写锁。
5. fieldDb也提供了`put`, `get`,`getFields`, `putFields`,`delete`接口。对前三者,简单调用`kvDb`中的对应接口(不涉及索引)。
对`putFields`,先判断是否有`fieldWithIndex`中有的字段,如果有,并对`kvDb`调用一个(多个)`put`,**但在写日志时一并加上索引日志写入**
`delete`逻辑一致
6. 针对索引的日志:为了保证两个数据库间的一致性,由`kvDb`的日志模块统一管理。这其中包含了两种chunk(kv写入和索引写入),在恢复时需要分别解析,决定往哪一个数据库中写入。索引写入的时机在4、5中的**加粗**部分,如何编码还有待设计。也就是说,`indexDb`本身的索引模块不再起到作用,项目后期可以修改关闭这一部分。
7. 对两个数据库的其他部分,理论上每个数据库内部的其他模块不会互相影响
# 3. 数据结构设计
`indexDb`的kv编码:**暂时考虑助教文档那种**
`IndexLog`的编码:字段名 加 类型(创/删)
具体设计模块化,实现时再具体考虑。
`indexDb`的kv编码:**暂时考虑助教文档那种**
区分日志中kv部分和index部分:**有待完成**
# 4. 接口/函数设计
`FieldDb`的对外接口函数之前已展示,这里补充一些子数据库需提供给`FieldDb`的抽象功能(暂时想到的):
```
class Db{
//原有部分和lab1部分
//kvdb需要:
pair<slice key, string fieldVal> FindKvsByField(string fieldName); //搜集索引需要的数据信息
status writeIndexLog(string fieldName, pair<slice key, string fieldVal>); //向indexDb put前先写日志
}
```
类内部实现的功能函数,具体实现过程中再抽象。
# 5. 功能测试
1. 基本的每个接口函数调用。
2. 创建/删除索引时的并发读写、并发创/删。
3. 数据库的恢复检查。
4. 性能测试。
# 6. 可能的挑战与解决方案
已想到的部分在之前已阐述,其余待发现。
已想到的部分在之前已阐述,其余待发现。
mark一些原代码(db中)的修改点:recover时日志解析修改,write时的日志写入可能要合并索引的写入。
# 7. 分工与进度安排
功能 | 完成日期 | 分工

Loading…
Откажи
Сачувај