ToplingDB 顺序扫描中 Value 的延迟加载

(一)背景ToplingDB 是 topling 开发的 KV 存储引擎,fork 自 RocksDB,进行了很多改造,其中最重要的部件是 ToplingZipTable, 是 BlockBasedTable 的代替品,性能更高而内存占用更低!(二)顺序扫描流程RocksDB 中,同一个 UserKey 会有多个版本的 Value,而特定时刻(snapshot)对外合法可见的 Value 只有一个,DBIter 将内部 {InternalKey, Value} 外化为 {UserKey, Value}。外化过程,就是按照 MVCC 的定义,将同一个 UserKey 的多个 {Seq, ValueType, Value} 转化为单个目标 Value。其实现细节相当复杂,我们先说正向扫描(已大幅简化):DBIter 的底层 Iter 正向扫描的输出中,同一 UserKey 的多个 {Seq, ValueType, Value} 是按 Seq 从大到小的跳过所有 Seq 大于 DBIter.snapshot 的 {Seq, ValueType, Value}然后就碰到了小于等于 snapshot 的 Seq 最大的 {Seq, ValueType, Value}如果 ValueType 是 kTypeValue,这就是我们想要的如果 ValueType 是 Delete,相当于这个 UserKey 不存在如果 ValueType 是 Merge,按 Merge 规则计算目标 Value在上述 3.1 中,DBIter 只需要将底层 Iter 返回的 Value 原封不动地返回出去即可,这里就存在着一个优化的机会:如果用户代码没有访问该 Value,那么 DBIter 也不需要访问底层 Iter 的 Value。这就是典型的延迟加载,但是 RocksDB 并没有做这个优化。RocksDB 的 InternalIterator 有成员函数 PrepareValue,TableReader::NewIterator 也有参数 bool allow_unprepared_value,看上去好像都是为延迟加载设计的。DBIter 也的确实现了一些延迟加载,不过仅限内部流程,例如外部不需要看到的那些 {Seq, ValueType, Value},就直接跳过了,不需要加载 Value。RocksDB 没有做的延迟加载是:用户能看到这个 {UserKey, Value},但是通过判断 UserKey,发现并不需要它的 Value。虽然理论上每个 UserKey 都可以有很多个 {Seq, ValueType, Value},但实际上对于绝大多数 UserKey,只有一个 {Seq, ValueType, Value},RocksDB 也对此作了 Seq 归 0 的优化:只要不存在小于 Seq 的 snapshot,Seq 就可以安全地归 0,从而减少存储空间。所以,RocksDB 没有做的这个延迟加载,实际上也是很有必要的。这个,在 ToplingDB 中已经实现了:
ToplingDB 顺序扫描中 Value 的延迟加载_第1张图片
例如,用户代码在 UserKey 中增加了一个 timestamp(相当于用户层 Seq),扫描时要跳过那些不可见的 timestamp。延迟加载的优势就体现出来了。RocksDB 很早就增加了对用户层 timestamp 的支持,但是因为各种各样的原因,在实践中,该功能并没有被广泛使用,大部分用户仍然自己处理用户层 timestamp。ToplingZipTable 中 Value 是分离存储,定点访问,逐条加载的,不访问,就不用加载,天生对延迟加载非常友好。这就又多了一个相比 BlockBasedTable 的优势!(三)反向扫描按说反向扫描逻辑应该跟正向扫描差不多,但是因为 RocksDB 通过 InternalKey Comparator 定义的顺序:反向扫描时看到的相同 UserKey 的 Seq 是从小到大的!这使得反向扫描要去看前一个(更小的)UserKey,或者当前 UserKey 的 {Seq, ValueType, Value} 中的 seq 已经大于 snapshot 了,才能确认目标 Value。又因为 Iter 只能看到当前位置上的 KV,于是 DBIter 就必须先把看到过的 Value 保存起来,才能在需要目标 Value 的时候,从历史记录中拿到它。所以,按照现有的机制,反向扫描是无法实现延迟加载的。这本质上是因为 RocksDB 把 (UserKey, SeqNum) 这样的二维 Key 通过 InternalComparator 转化(抽象)成了一维序列而导致的,实际上,按照 Topling MemTable, Fast Table, Zip Table 的内部结构,可以高效地实现反向扫描。(四)Zero CopyToplingDB 的 Zero Copy 比延迟加载更加延迟,延迟到了访问 Value 所指内存的时候。所以,即便在反向扫描中,Zero Copy 仍能起到延迟加载的效果。不过 Value Zero Copy 与 Value 压缩是互斥的,要 Zero Copy,就要舍弃压缩。在 ToplingDB 中,ToplingZipTable(不压缩时) 和 SingleFastTable 都支持 Zero Copy。【完】

你可能感兴趣的:(ToplingDB 顺序扫描中 Value 的延迟加载)