Pārlūkot izejas kodu

有关KV分离的简要介绍

cyq
cyq pirms 8 mēnešiem
vecāks
revīzija
f511b1c4d7
1 mainītis faili ar 88 papildinājumiem un 1 dzēšanām
  1. +88
    -1
      README.md

+ 88
- 1
README.md Parādīt failu

@ -834,4 +834,91 @@ fieldDb功能实现(没有并发和恢复)|12.10|李度、陈胤遒
lab2测试、并发完成|12.20|李度
恢复|12.25|李度、陈胤遒
整体系统整合+测试|12.28|李度、陈胤遒、高宇菲
性能测试|1.1|李度、陈胤遒、高宇菲
性能测试|1.1|李度、陈胤遒、高宇菲
## 7. KV分离
由于在讨论的时候我们想到了一种感觉非常思路清晰、实现简单的实现kv分离的方案,在基本完成了实验报告后,我们抽空花了一天半基本上实现了kv分离,代码总计700行左右下面对于设计进行最简单的介绍
### 7.1 KVLog
我们实现kv分离的主要途径是使用KVLog和FilePointer。KVLog和SSTable在生成的时候是一一对应的,但是并不随着SSTable的合并而合并。KVLog是一个如下结构的文件类型:
```
Batchsize|WriteBatch|Batchsize|WriteBatch|......
```
`FilePointer`包含三个信息,分别是KVLog的文件编号,偏移量以及信息的长度。
从逻辑结构来看,非常类似于leveldb的log,两者本质上存储的都是一系列的WriteBatch。但是不同的是,leveldb的log会每4KB对于内容进行切分,这会导致无法直接通过`(偏移量,长度)`读出内容,而KVLog是连续的存储,是可以直接通过`(偏移量,长度)`读出内容的。
由于leveldb的写入原子性是通过基于log文件的结构完成的,且log在写入和恢复的路径中被大量的使用,因此我们不直接使用KVLog替换log,而是先将WriteBatch写入KVLog,然后将该WriteBatch对应的`FilePointer`写入log中。这样子我们只要对于涉及log的代码进行最简单的更改即可。
之后,我们对于写入到memtable和SSTable中的内容进行处理,将所有的value替换成相应的`FilePointer`。
以上就是对于写入流程的处理,部分核心代码如下:
```c++
Status DBImpl::Write(const WriteOptions& options, WriteBatch* updates) {
/******************************/
FilePointer fp;
//1. 将WriteBatch写入到kvlog中
status = kvlog_->AddRecord(WriteBatchInternal::Contents(write_batch), fp);
//2. 将writebatch的filepointer写入到log中
// status = log_->AddRecord(WriteBatchInternal::Contents(write_batch));
char rep[8 * 3];
EncodeFP(fp, rep);
status = log_->AddRecord(Slice(rep, 3 * 8));
bool sync_error = false;
if (status.ok() && options.sync) {
status = logfile_->Sync();
if (!status.ok()) {
sync_error = true;
}
}
//3. 根据write_batch里面的内容,构建kp_batch
WriteBatchInternal::ConstructKPBatch(tmp_kp_batch_, write_batch, fp);
WriteBatchInternal::SetSequence(tmp_kp_batch_,temp_seq + 1);
if (status.ok()) {
status = WriteBatchInternal::InsertInto(tmp_kp_batch_, mem_);
}
/******************************/
}
class KVLog; //用于KVLog的写入
class KVLogReader;//用于读取KVLog中的每一个键值对以及相应的sequence
struct FilePointer {
uint64_t FileNumber;
uint64_t FileOffset;
uint64_t Size;
};
```
### 7.2 KVLog的管理
KVLog的管理主要参考的是SSTable的方式,采用多版本并发控制的思路。原因主要有两点:1. 有利于KVLog回收的实现;2.可以大量的复用或者参考SSTableFile的实现方式。核心代码如下:
```c++
class VersionEdit {
/*****************************/
void AddKVLogs(uint64_t file) {
FileMetaData f;
f.number = file;
new_kvlogs_.push_back(f);
}
void RemoveKVLogs(uint64_t file) {
deleted_kvlogs_.insert(file);
}
std::set<uint64_t> deleted_kvlogs_;
std::vector<FileMetaData> new_kvlogs_;
/*****************************/
}
class VersionSet{
/*****************************/
void AddLiveKVLogs(std::set<uint64_t>* live_kvlogs);
std::vector<FileMetaData*> kvlogs_;
void Apply(const VersionEdit* edit);
void SaveTo(Version* v);
/*****************************/
}
```
### 7.3 KVLog的回收
我们观察到Level0的SSTable是允许重叠的。因此,我们将KVLog的回收放在后台,同时禁止小合并将新生成SSTable放于L0以下的位置,这样可以保证L0以下的level不变。然后只回收KVLog不与mem、imm和L0SSTableu对应的kvlog。回收的时候新构建一个放在L0的SSTable以及对应的KVLog。新生成的SSTable包含的key和原来的sequence完全相同,value指向新的KVLog。这样子,随着之后的合并,自然就能够完成回收的操作也不会和并发的写入发生冲突。代码主体是`bool DBImpl::CollectKVLogs()`.
以上就是对于我们实现KV分离方式的最简要的介绍

Notiek ielāde…
Atcelt
Saglabāt