根据前面的分析,背景线程的主体工作在BackgroundCompaction函数中完成。这个函数主要完成以下两个工作:
- 如果imm_非空,则将imm_写入到磁盘中生成新的sstable文件
- 对level中的文件进行合并。合并的目的主要是避免某个level中sstable文件过多,并且可以通过合并的过程删除掉过期的key-value和被用户删除的key-value。
这篇文章主要是从BackgroundCompaction函数开始,分析level中文件合并的过程
void DBImpl::BackgroundCompaction() { //在背景线程中执行
mutex_.AssertHeld();
if (imm_ != NULL) {
CompactMemTable(); //将imm_写到level 0
return;
}
这部分主要是完成上面所说的第一个工作,将imm_写入level 0 中。因为这个函数需要处理很多整个数据库共享的数据结构,比如imm_,versions_等,因此必须保证在临界区中执行。
Compaction* c;
bool is_manual = (manual_compaction_ != NULL);
InternalKey manual_end;
if (is_manual) {
ManualCompaction* m = manual_compaction_;
c = versions_->CompactRange(m->level, m->begin, m->end);
m->done = (c == NULL);
if (c != NULL) {
manual_end = c->input(0, c->num_input_files(0) - 1)->largest;
}
Log(options_.info_log,
"Manual compaction at level-%d from %s .. %s; will stop at %s\n",
m->level,
(m->begin ? m->begin->DebugString().c_str() : "(begin)"),
(m->end ? m->end->DebugString().c_str() : "(end)"),
(m->done ? "(end)" : manual_end.DebugString().c_str()));
}
这部分是在设置manual_compaction_ 的时候被执行,它主要是用于测试数据库的compaction功能,用于debug,数据库实际运行时一般不会运行这段代码,这里且略过,不过从功能实现上和后面的分析也是一样的。
else {
c = versions_->PickCompaction(); //找出最适合compaction的level
}
这个条件分支是一个重点,它负责从数据库中选出适合进行compaction的level。它将返回可以进行compaction的level中的文件元信息,这些元信息存储在Compaction类中
可以看一下compaction的数据成员:
这个compaction将会合并level和level+1中的部分文件。
如果我们深入到PickCompaction就会看到leveldb是怎么选择需要进行compaction的level的。
leveldb首先从当前的versions中选择一个最适合进行compaction的level,这里的最适合主要是通过版本控制里面的compaction_score_变量进行衡量,同时每个版本都会有一个变量跟踪当前版本中最适合进行compaction的level(current_->compaction_level_)。除此之外,leveldb为每个level中的文件维持一个 数组compact_pointer_,这个compact_pointer_[level]指向当前level中上次被compaction的最大key的值,因此下次对这个level进行compaction时,就要从key大于compact_pointer_[level]的文件开始,而且我们可以看到,通常是选择第一个largest key大于compact_pointer_[level]的文件作为当前level需要进行compaction的文件。
从当前的level选择出适合进行compaction的文件后,我们就可以通过版本控制中的元信息找到这个文件所包含的key的最大值和最小值,通过这个最大值和最小值,我们就可以找到level+1中有哪些文件和level中的这个文件的key可能有重合,然后将这些文件和level中的那个文件进行compaction,compaction后的文件放入到level+1中。需要注意的是,每次compaction可能生成多个文件,而不是一个compaction只生成一个文件。
回到上面的compac