压缩过滤

RocksDB提供了一种基于后台自定义逻辑删除或修改键/值对的方法。 它便于实现自定义垃圾收集,比如基于TTL删除过期key,或者在后台删除一系列key。它还可以更新现有键的值。

要使用压缩过滤器,应用程序需要实现在rocksdb/compaction_filter.h中找到的CompactionFilter接口,并将其设置为ColumnFamilyOptions。 另外,应用程序也可以实现CompactionFilterFactory接口,它提供了为每个(子)压缩创建不同的压缩过滤器实例的灵活性。 压缩过滤器工厂还通过给定的CompactionFilter:: context参数从压缩中了解一些上下文(无论是完整的压缩还是手动压缩)。 工厂可以根据上下文选择返回不同的压缩过滤器。

  1. options.compaction_filter = new CustomCompactionFilter();
  2. // or
  3. options.compaction_filter_factory.reset(new CustomCompactionFilterFactory());

提供压缩过滤器的两种方法也有不同的线程安全要求。如果向RocksDB提供一个压缩过滤器,那么它必须是线程安全的,因为多个子压缩可以并行运行,而且它们都使用相同的压缩过滤器实例。 如果提供了压缩过滤器工厂,则每个子压缩都将调用工厂来创建压缩过滤器实例。因此,可以保证从单个线程访问每个压缩过滤器实例,并且不需要压缩过滤器的线程安全性。 不过,可以通过子压缩并发地访问压缩过滤器工厂。

尽管刷新是一种特殊的压缩类型,但在刷新期间不会调用压缩过滤器。

有两组API可以通过压缩过滤器实现。Filter/FilterMergeOperand API提供了一个简单的回调,让压缩知道是否过滤掉键/值对。 FilterV2 API通过允许更改值或从当前键开始删除一系列键来扩展基本API。

每次(子)压缩从其输入中看到一个新键,当该值为正常值时,它调用压缩过滤器。根据压实滤波器的结果:

  • 如果它决定保留key,什么也不会改变。
  • 如果它请求过滤键,则用删除标记替换该值。注意,如果压缩的输出级别是底层,则不需要输出删除标记。
  • 如果它请求更改值,则该值将被更改的值替换。
  • 如果它通过返回kremoveandskiptill请求删除一系列键,则压缩将跳过到skip_until(意味着skip_until将是压缩的下一个可能的键输出)。 这一点很棘手,因为在这种情况下,压缩不会为它跳过的键插入删除标记。这意味着键的旧版本可能会因此重新出现。 另一方面,如果应用程序知道没有旧版本的密钥,或者可以重新出现旧版本的密钥,那么简单地删除密钥会更有效。

如果压缩的输入中有多个相同键的版本,则对最新版本只调用一次压缩过滤器。如果最新版本是删除标记,则不会调用压缩过滤器。 但是,如果压缩的输入中没有包含删除标记,则可能对已删除的键调用压缩过滤器。

当使用merge时,每个merge操作数调用压缩过滤器。在调用合并操作符之前,将压缩过滤器的结果应用于合并操作数。

在发布6.0之前,如果有一个比键/值对晚的快照,RocksDB总是试图阻止键/值对被压缩过滤器过滤,以便用户可以从快照中保留相同的视图,除非压缩过滤器返回IgnoreSnapshots() = true。 但是,在意识到该特性存在不易修复的bug后,从6.0开始删除该特性。自从6.0版启用了压缩过滤器以来,RocksDB总是对任何键调用过滤,即使它知道这会使快照不可重复。