WiscKey:LSM-Tree 写放大优化

第一次看完 WiscKey 这篇论文,觉得写得很接地气,很实用,很通俗易懂。

这里简单记录一下。

WiscKey 简介

WiscKey 的提出,主要是为了优化 LSM-Tree 的写放大问题。此前已经有不少论文讨论过这个问题,如 LSM-trie 和 PebblesDB,但是大部分优化方法都不是很彻底——简单说就是,优化效果太差,或者不够通用。WiscKey 提出的是一种比较通用、效果明显且简单易懂的方法。

其实 WiscKey 优化写放大的原理非常简单:在 LSM-Tree 上做 key 和 value 的分离存储

LSM-Tree 写放大的根本原因是,compaction 时为了保证数据有序进行大量数据(key 和 value)重写。实际上,需要保持有序的只有 key,如果将 key 和 value 分开存储,compaction 重写数据的时候,就只需要重写 key(和 value 的位置,简称 vpos)。这在 key size (简称 ksize)远小于 value size (简称 vsize)的场景(现实场景基本都是这样)降低写放大的效果十分明显。

另一方面,当 vsize 比较小的时候,重写 value 这部分的开销就比较小,key-value 分离存储带来的好处就不足以抵消它带来开销。Key-value 分离存储带来的额外开销 —— 读写一个 key-value 需要操作不同文件。这在 range query 的场景下,会产生多次随机 I/O,影响比较明显。如下图所示。


WiscKey:LSM-Tree 写放大优化_第1张图片
WiscKey-Range-Query-Performance.PNG

对顺序 range query:

  1. 当 vsize 比较小的时候,LevelDB 的性能和 WiscKey 很接近。因为是顺序 range query,WiscKey 的预读缓存功能可以有效地提升性能。
  2. 当 vsize 逐渐变大,LevelDB 的性能急剧下降,WiscKey 的性能比较平稳,明显优于 LevelDB。因为 value 变大,导致 LevelDB 一个 block 可以存储的 kv pair 变少了,cache 效果变差。

对于随机 range query:

  1. 当 vsize 比较小的时候,LevelDB 的性能优于 WiscKey。因为是随机 range query,范围应该比较小,WiscKey 的预读缓存功能没能很好发挥作用。
  2. 当 vsize 逐渐变大,WiscKey 的性能很快超越了 LevelDB。

Key-value 分离存储后,LSM-Tree 索引结构上只存储 key 和 vpos,实际的 value 存储在另一个 append-only 的 log 文件中,称为 vlog。总体看来,WiscKey 很像一个用 LSM-Tree 做索引的 BitCask。

WiscKey 带来的好处

  1. LSM-Tree Compaction 不需要重写 value,大大减小写放大。
  2. LSM-Tree 不存储 value,体积更小,一个 block 能存更多的 key,有利于减少读 LSM-Tree 的 I/O。
  3. LSM-Tree 的体积小,cache 效果应该会更好。LSM-Tree 的上面几层基本都可以 cache 在内存中。

WiscKey 面临的问题和挑战

虽然减小了写放大,提升了 key 的密度,进而优化了 LSM-Tree 的 cache 效果。但是 key-value 分离存储也给 WiscKey 带来一些问题和挑战。下面简单分析一下。

  1. 当发起一次 range query 的时候,最终会转化成多次针对 vlog 的随机读。这种场景下, WiscKey 会通过多个后台线程对后面的多个数据进行预读,并缓存起来,充分利用 SSD 内部的并行性。预读缓存对于范围比较大的 range query 效果可能比较明显,对于小范围的随机 range query,可能发挥不了什么作用。

  2. Key 和 value 分开存储后,怎么保证 key 和 value 的一致性呢?针对这个问题,WiscKey 采用的方案是,先写 vlog,再写 LSM-Tree 。如果写 vlog 成功,写 LSM-Tree 失败,则写失败。此时,vlog 上的值不会被暴露出去,只需后面进行垃圾回收。这里如果要保证数据一致性和可靠性,则写 vlog 后要刷盘,写 LSM-Tree 后也要 WAL 刷盘。这里有两次刷盘开销,通过 WAL 和 vlog 的合并,可以优化成一次。

  3. vlog 的垃圾回收。垃圾回收需要扫描数据,根据数据结构,有两个方向可以考虑:

    1. 扫 LSM-Tree。根据 LSM-Tree 把 vlog 中有效的内容都提取出来。这个方法不太适合在线服务,需要一次性重写整个 vlog,开销比较大。重写 vlog 的同时外部依然会更新 LSM-Tree 和 vlog,数据一致性不太好处理。
    2. 扫 vlog。为了扫描 vlog,vlog 不能只存储 ,需要存储 。根据扫出来的每一个 ,通过 LSM-Tree 验证其有效性。为了不影响在线服务,WiscKey 每次取出 vlog 最旧的一部分数据(几 MB),通过 LSM-Tree 进行验证,过期则丢弃,有效则 append 到 vlog,并修改 LSM-Tree(类似执行一次写入操作),并删除这部分回收验证过的数据,通过 fallocate() 释放无效的文件磁盘空间。

参考文档

  • WiscKey: Separating Keys from Values in SSD-conscious Storage
  • LSM-trie: An LSM-tree-based Ultra-Large Key-Value Store for Small Data
  • PebblesDB: Building Key-Value Stores using Fragmented Log-Structured Merge Trees

你可能感兴趣的:(WiscKey:LSM-Tree 写放大优化)