GetRandomInteranlKeysAppend

在文档 Level 1 Sub Compaction 中,我们知道,L0 + L1 -> L1 compact 会启用 subcompact,而 subcompact 又是怎么划分的呢?

在 RocksDB 中,划分 subcompact 是通过取每个 input SST 文件的最大最小 key,得到一个有序的 key 集合,然后让每个 subcompact 覆盖相同数量的 key。

当 input SST 数量很多的时候,这个机制可以工作得不错,但是,当 L0 SST 很大(≈ write_buffer_size, 例如 512M),而 L1 SST 很小(target_file_size_base, 例如 32M),这样的机制很容易产生数据偏斜(skew),导致 subcompact 的尺寸差异巨大,从而整个 compact 耗时就依赖于最大的那个 subcompact。

所以,ToplingDB 在 TableReader 中添加了一个函数:

1
2
3
4
virtual bool GetRandomInteranlKeysAppend(
size_t num, std::vector<std::string>* output) const {
return false; // indicate not implemented
}

函数名中 Append 后缀表示追加到 output,而不是覆盖 output。

有了这个函数,我们就能从每个 SST 拿到多个 Key,从而将 subcompact 分得比较均匀!

L0 SST 尺寸远大于 L1 SST 这种配置,是 ToplingDB 的推荐用法,因为:

  1. 减小了读放大(L0 SST 数量少了)
  2. 减小了写放大(L0 上单个 SST 是一个 sorted run,L1 上全部 SST 组成一个 sorted run,不同 sorted run 的尺寸大致相等)
  3. L0 + L1 -> L1 启用 subcompact,充分并行

在 ToplingDB 中,通过这些机制,将 L0 以最快的速度推到 L1,并且,因为 L1 文件较小,所以 L1 的文件数量很大,然后再利用分布式 Compact,L1->L2 的 compact 就可以充分并行起来(max_background_compactions),反正都是转移到 Compact 集群,不消耗 DB 结点的计算资源。

为了尽可能减轻 DB 结点的负载,L0 和 L1 的 SST 都不压缩,并且使用 ToplingFastTable(而不是使用 BlockBasedTable);从 L2 开始,就使用 ToplingZipTable + 分布式 Compact