Rocksdb提供一种方法,用于自定义逻辑在后台线程删除或者修改 key/value对。这在用于自定义垃圾回收的时候非常方便,比如根据TTL删除超时的key,或者在后台删除一个区间的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提供一个简单的回调,用于告知压缩过程是否需要过滤一个key/value对。FilterV2 API通过允许修改value或者删除从当前的key开始的一个范围的key,来拓展基础API。

    每当一个子压缩看到一个输入进来的新的key,并且他的value是普通value,他就调用压缩过滤器。根据压缩过滤器的结果:

    • 如果他决定保留这个key,什么也不会发生。
    • 如果他要求过滤这个key,其值会被替换成一个删除标记。注意,如果输出的压缩层是最底层,就不需要删除标记了。
    • 如果要求修改值,那么新的值将会替换旧的值
    • 如果返回kRemoveAndSkipUntil,要求删除一个范围的key,压缩会跳过,直到skip_until(意味着skip_until会是下一个可能的压缩输出的key)。这个有点操作有些偷巧,因为这里压缩器没有给跳过的key插入删除标记。这意味着旧的版本的key值会在结果里重新出现。另一方面,简单地丢弃这些key可能更加高效,如果应用程序知道这些key没有更老的版本,或者这些旧的key重新出现可以接受。

    如果压缩的输入里面有同一个key的多个版本,压缩过滤器只会对最新的版本调用一次。如果最新的版本是一个删除标记,压缩过滤器也不会调用。然而,压缩过滤器可能针对一个已经删除的key被调起,如果这次压缩的输入里面没有把这个key标记为删除。

    当合并被使用,压缩过滤器会在每个合并操作的时候调起。压缩过滤器的结果会在合并操作符之前被应用到合并操作。

    如果在key/value对之后创建了快照,这个key/value对酒不能被过滤,而且压缩过滤器也不会被调起。