10225501448 李度 10225101546 陈胤遒 10215501422 高宇菲
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 

6.5 KiB

3DB

分别为kvDB,indexDB,metaDB。其中,前两个和之前存储内容相同,metaDB用来维护与并发控制相关的内容 为了达成一致性,这里需要要有一个全局唯一的seq或者timestamp。这个信息编码在userkey中,比较器要进行对应修改,metaDB要按照先后顺序排列,另外两个不用考虑timestamp

具体操作

这里将操作类型进行分类:普通put和get,索引put和get,创建(删除)索引。 接下来将根据这个分类进行阐述

普通put和get

普通get直接调用kvDB的get 普通put要判断当前是否进行索引put和创建(删除)索引操作。如果有,则需要进入taskqueue等待。

索引put

如果当前字段中没有需要创建索引的,那么就是普通put

如果当前没有正在创建(修改)的索引或者之前的对于同一个key的索引put,则首先向metaDB写入标记(key,creating/deleting),表示事务的开始。然后构建请求,并向kvDB和indexDB分别写入。所有的请求的时间戳都和metaDB中标记的时间戳相同。全部完成之后,将之前在metaDB中的标记delete,表示事务的结束。

如果当前有正在创建(修改)的索引或者之前的对于同一个key的索引put,则判断本次put是否含有对应的索引,如果没有则按上面一段的操作进行。如果含有,则加入之前设计中的taskqueue,在索引创建完成后会进行处理。

想法:索引put涉及到了indexDB和kvDB两者之间的原子性

创建(删除)索引

在进行这个操作之前对metaDB写入一个标记(field,creating/deleting),表示在field上创建(删除)索引操作的事务的开始。(注:这里的key是包含了时间戳或者seq的)。

之后扫描kvDB,构建相应请求,对indexDB进行写入,这里也是通过writebatch写入的。写入完成之后,将之前的标记清除,表示当前的事务结束了。之后对于taskqueue里面的请求进行处理,完成之后,唤醒taskqueue中的请求。

想法:创建(删除)索引这个操作实际上只对indexDB进行了写入请求,并不涉及indexDB和kvDB两者之间的一致性

索引get

如果没有索引,则返回;如果索引正在创建,则存到taskqueue中,在索引创建完成之后进行处理(这里或许也可以直接返回);如果存在索引则将请求发往indexDB。

一致性,崩溃恢复(recovery)

创建(删除)索引

如果meta中存在记录(field,creating/deleting),则表示在创建(删除)索引的过程中的某个时间节点崩溃了。

如果在metaDB写入标记后崩溃,indexDB写入前崩溃。由于这个操作只涉及indexDB,且与当前创建(删除)索引相关的写入请求都被阻塞了,所以只要再扫描一遍全部的kvDB构造index写入请求就可以了。

如果是在indexDB写入完成之后崩溃,这实际上已经完成了创建(删除)索引操作,所以把metaDB中的标记清除即可。

这里最主要的问题在于如何判断崩溃时间点。对于写入标记前后通过metaDB中的记录判断。对于是否对indexDB完成写入通过能否在indexDB中找到对应的索引判断,因为索引是一个writebatch整体写入的,有原子性。

索引put

索引put涉及到kvDB和indexDB写入的一致性,这一点通过时间戳机制来保证。

如果metaDB中有记录(key,creating/deleting),那么表明索引put过程中的某个时间节点崩溃了。由于kvDB和indexDB的写入是并发进行的,所以可能会出现四种情况:

  1. kvDB和indexDB写入均未完成
  2. kvDB写入完成而indexDB未完成。
  3. kvDB写入未完成而indexDB写入完成
  4. 两者写入都完成,但是没有清除记录

kvDB的写入情况的判断如下:通过metaDB中的记录的key,查询kvDB。如果是creating操作且记录不存在或者得到的时间戳不等于metaDB记录的时间戳,则表明写入未完成;如果是deleting,如果存在记录,则表明写入未完成。

indexDB的写入情况判断如下:扫描indexDB,如果是creating操作且记录不存在或者得到的时间戳不等于metaDB记录的时间戳,则表明写入未完成;如果是deleting操作,如果存在对应的二级索引,则表明写入未完成。如果在kvDB中能够得到相应的kv,可以通过kvDB中的kv查询二级索引。

分别讨论creating和deleting操作下的四种情况的崩溃恢复的过程:

  1. 如果是creating,则清除metaDB中的记录;如果是deleting,则继续delete
  2. 如果是creating,则根据kvDB的写入构造请求写入indexDB;如果是deleting,则遍历indexDB删除对应的索引
  3. 如果是creating,且kvDB中有旧值,则将indexDB中所有相关的字段清除后根据旧值创建索引;如果是deleting,删除kvDB中的kv对
  4. 直接清除记录

当然,这些处理的方式会比较的细,总的来讲,只要kvDB完成写入,那么indexDB就可以完成更新;如果写入未完成,那么indexDB就需要用某种方式回滚。

全写入方案

不用时间戳,全部写入metaDB作为log,然后再写入kvDB和indexDB

整体架构

基于request(类似于writer)来处理并发的请求。对于创建和删除索引操作,包含一个pending队列,来维护会受到影响的请求。

有关实现的部分

  1. 对于metaDB中存入数据的编码部分放在了metakv文件中

TODO List

  1. index和kv的写入应该放在两个线程中同时写入,这里为了实现的方便,暂时先后完成

  2. 原版的env中的Schedule是使用单例模式,也就是所有的数据库都只有一个线程,我们这里 需要所有的数据库都有属于自己的线程,且可能不止一个,因此需要实现类似于线程池的东西

一些想法

  1. 根据对于某一个Field搜索的频率和耗时,自动的创建索引,且这个索引会在长时间不用后被清除

有关KV分离的想法

复用原有的log,将log信息加入到version信息中,需要更改version edit,version等内容 log带上编号,从小到大的进行垃圾回收 垃圾回收过程中形成的新的写入保留原有的seq放入L0

KV分离核心的困难之一在于垃圾回收的并发控制。我的核心想法是在回收log的时候,不进行合并操作,将 回收得到的东西直接保留seqno放进L0。由于L0本身就是无序的,如果在垃圾回收的过程中产生了并发写入, 新的写入也只会写入到L0,这样只要等待下一次的合并就行了。