Tuning|Tuning RocksDB - Prefix Extractor
在 TiKV 里面,我们在 Write 和 Raft column family (CF) 上面使用了 prefix extractor 机制,用来加速数据的读取和插入插入。
通常我们直接将 key 插入到 RocksDB 里面,不做任何改变,所有的 key 是按照字节序依依次排列的,Bloom filter 里面也是对整个 key 的判断。但有些时候,这些机制并不适合。
Write CF
譬如对于 TiKV 的 Write CF 来说,我们的 key 的组织方式是 key + version 的方式,譬如这样:
key1 - ver 1
key1 - ver 2
......
key1 - ver n
key2 - ver 1
key2 - ver 2
......
key2 - ver n
......
在 Write CF 的逻辑里面,我们只会用一个不带 version 的 key 来判断这个 key 是否存在,所以我们只能用
seek(key)
的方式,然后看得到的 key 去掉 version 之后是否就是我们需要的。seek 在数据量大的时候会存在明显的性能问题,因为它要在所有的 level 上面都要定位一下,但现在的机制我们又没法改成 get,虽然后面可能重构,但现在首要机制就是保证 seek 的性能。如果 seek 也能用 bloom filter,那么就会减少很多无用的文件定位了,所以我们在这里使用了 prefix_extractor
机制,将不带版本的 key 放到了 bloom filter 里面,提升 seek 的性能。Raft CF 而对于 Raft CF 来说,key 的格式类似
region ID + log ID
的方式,譬如:region 1 : log 1
......
region 1 : log n
region 2 : log 1
......
region 2 : log n
......
即使系统里面有太多的 region,但同一个时刻同时活跃的 region 数量并不多,所以通常我们只会在一段时间处理一批 region 的 Raft log。也就是在 RocksDB 里面 memtable 的实际 region 数量是不会特别多的,但因为这些 region 很活跃,所以插入 Raft log 的性能压力很大。
同时我们也观察到,对于一个 region 来说,它的 log 插入是顺序的,也就是第一次插入 log 100,下一次一定是 log 101,也就是一定在 log 100 的后面,这个是 Raft 保证的,所以我们可以使用
memtable_insert_with_hint_prefix_extractor
机制,使用一个 hint 指针放在 log 100 那个地方,这样当下一次需要插入 log 101 的时候,直接从 hint 下一个位置插入,而不需要再在 memtable 里面进行多次比较了。Prefix Extractor 上面两个优化都是使用了 prefix extractor ,Prefix extractor 是一个 SliceTransform object,定义如下:
// SliceTranform is a generic pluggable way of transforming one string
// mainly used for prefix blooms.
pub trait SliceTransform {
// Extract a prefix from a specified key
fn transform<'a>(&mut self, key: &'a [u8]) -> &'a [u8];
// Determine whether the specified key is compatible with the logic
// specified in the Transform method. This method is invoked for every
// key that is inserted into the db. If this method returns true,
// then Transform is called to translate the key to its prefix and
// that returned prefix is inserted into the bloom filter. If this
// method returns false, then the call to Transform is skipped and
// no prefix is inserted into the bloom filters.
fn in_domain(&mut self, key: &[u8]) -> bool;
// This is currently not used and remains here for backward compatibility.
fn in_range(&mut self, _: &[u8]) -> bool {
true
}
}
我们重点关注
transform
和 in_domain
, transform
就是提取 key 的 prefix,注意,虽然这里说明的是前缀 prefix,实际我们也可以提取后缀 postfix,或者中间某一段。in_domain
用来判断这个 key 是否符合提取的要求,如果返回了 true,则表明可以使用 transform
提取 prefix 并插入到 bloom filter 里面。可以看到,无论是自定义 prefix extractor 还是使用,都是非常简单的,但需要注意几个地方:
- 如果我们仍然要支持完整的 key Get 操作,那么需要设置
whole_key_filtering
为 true,这样不光 prefix 会加入到 bloom filter 里面,整个 key 也会加入进去。 - 如果我们仍然需要 iterator 顺序 scan 所有的数据,需要将
total_order_seek
设置为 true,不然 scan 出来的数据顺序就不对。
推荐阅读
- 解决升级android studio 3.2.1后 "No toolchains found in the NDK toolchains folder for ABI with prefix
- 多行文本省略号样式丢失,以及控制台显示autoprefixer警告'Autoprefixer applies control comment to whole block, not to ne
- Error:No toolchains found in the NDK toolchains folder for ABI with prefix: mips64el-linux-android(示
- Android Studio -No toolchains found in the NDK toolchains folder for ABI with prefix: mips64el-lin