RocksDB 的常用调优参数

本文详细介绍了RocksDB的关键参数及其影响,包括并行性、通用配置、flush及level-style compaction策略等,有助于理解RocksDB的工作原理及优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RocksDB 的参数以其数据多和复杂著称,要全部弄懂也要费一番功夫,这里也仅仅会说一下我们使用的一些参数,还有很多我们也需要后面慢慢去研究。

Parallelism

RocksDB 有两个后台线程,flush 和 compaction,两个都可以同时并行执行。在优先级上面,flush 是 HIGH,而 compaction 是 LOW,也就是 flush 的优先级会比 compaction 更高,这也很容易理解,如果数据都没有 memtable flush 到 level 0,后面也没法做 compaction。我们可以设置 flush 和 compaction 的最大线程数:

  • max_background_compaction:最大 compaction 线程数,默认是 1,但通常我们会调大,不然 compaction 会忙不过来
  • max_background_flushes:最大 flush 线程数,默认是 1。

General

  • filter_policy:也就是 bloom filter,通常在点查 Get 的时候我们需要快速判断这个 key 在 SST 文件里面是否存在,如果 bloom filter 已经确定不存在了,就可以过滤掉这个 SST,减少没必要的磁盘读取操作了。我们使用 rocksdb::NewBloomFilterPolicy(bits_per_key) 来创建 bloom filter,bits_per_key 默认是 10,表示可能会有 1% 的误判率,bits_per_key 越大,误判率越小,但也会占用更多的 memory 和 space amplification。
  • block_cache:为了加快从文件读取数据的速度,RocksDB 会将 block 缓存,虽然操作系统也有 OS cache,但通常,block cache 是缓存的没有被压缩的 block,而 OS cache 则是缓存的已经压缩好的 block。现在 RocksDB 也支持 direct IO 模式,这样就不会有 OS cache 了,但我们还没有使用过。另外,RocksDB 也支持一种 compressed block cache,类似 OS cache 的机制,但我们现阶段也没有使用过。通常我们会使用 rocksdb::NewLRUCache(cache_capacity, shard_bits) 来创建一个 LRU cache。
  • max_open_files:RocksDB 会将打开的 SST 文件句柄缓存这,这样下次访问的时候就可以直接使用,而不需要重新在打开。当 缓存的文件句柄超过 max_open_files 之后,一些句柄就会被 close 掉。如果使用 -1,RocksDB 将一直缓存所有打开的句柄,但这个会造成比较大量的内存开销,尤其是在内存较小的机器上面,很容易造成 OOM。
  • block_size:RocksDB 会将一批 data 打包放到一个 block 里面,当需要访问某一个 key 的时候,RocksDB 会将整个 block 都 load 到内存里面。一个 SST 文件会包含很多个 block,每个 SST table 都包含一个 index 用来快速定位到对应的 block。如果 block_size 越大,那么一个 SST 文件里面 block 的个数就越少,这样 index 占用的 memory 和 space amplification 就越小,但这样就会增大 read amplification。(为啥会读放大?没觉得啊,感觉只不过blockcache读上去的一个块比较大而以)

Flush

对于新插入的数据,RocksDB 会首先将其放到 memtable 里面,所以 RocksDB 的写入速度是很快的。当一个 memtable full 之后,RocksDB 就会将这个 memtable 变成 immutable 的,然后用另一个新的 memtable 来处理后续的写入,immutable 的 memtable 就等待被 flush 到 level 0。也就是同时,RocksDB 会有一个活跃的 memtable 和 0 或者多个 immutable memtable。对于 flush,我们需要关注:
- write_buffer_size:memtable 的最大 size,如果超过了这个值,RocksDB 就会将其变成 immutable memtable,并在使用另一个新的 memtable。
- max_write_buffer_number:最大 memtable 的个数,如果 active memtable full 了,并且 active memtable 加上 immutable memtable 的个数已经到了这个阀值,RocksDB 就会停止后续的写入。通常这都是写入太快但是 flush 不及时造成的。
- min_write_buffer_number_to_merge:在 flush 到 level 0 之前,最少需要被 merge 的 memtable 个数。如果这个值是 2,那么当至少有两个 immutable 的 memtable 的时候,RocksDB 会将这两个 immutable memtable 先 merge,在 flush 到 level 0。预先 merge 能减小需要写入的 key 的数据,譬如一个 key 在不同的 memtable 里面都有修改,那么我们可以 merge 成一次修改。但这个值太大了会影响读取性能,因为 Get 会遍历所有的 memtable 来看这个 key 是否存在。

Level Style Compaction

RocksDB 默认的将 SST 文件放在不同的 level,自然就是用的 level style compaction。Memtable 的被 flush 到 level 0,level 0 有最新的数据,其他更上层的 level 则是有老的数据。Level 0 里面的 SST 文件可能会有重叠,也就是不同的 SST 文件保护的数据 key range 会重叠,但 level 1 以及之上的 level 则不会重叠。对于一次 Get 操作来说,通常会在所有的 level 0 文件里面检查是否存在,但如果在其他层,如果在一个 SST 里面找到了这个 key,那么其他 SST 都不会包含这个 key。每一层都比上一层大 10 倍,当然这个是可以配置的。

一次 compaction 会将 level N 的一些文件跟 level N + 1 里面跟这些文件重叠的文件进行 compact 操作。两个不同的 compaction 操作会在不会的 level 或者不同的 key ranges 之间进行,所以可以同时并发的进行多个 compaction 操作。

在 level 0 和 level 1 之间的 compaction 比较 tricky,level 0 会覆盖所有的 key range,所以当 level 0 和 level 1 之间开始进行 compaction 的时候,所有的 level 1 的文件都会参与合并。这时候就不能处理 level 1 到 level 2 的 compaction,必须等到 level 0 到 level 1 的 compaction 完成,才能继续。如果 level 0 到 level 1 的速度比较慢,那么就可能导致整个系统大多数时候只有一个 compaction 在进行。

Level 0 到 level 1 的 compaction 是一个单线程的,也就意味着这个操作其实并不快,RocksDB 后续引入了一个 max_subcompactions,解决了 level 0 到 level 1 的 compaction 多线程问题。通常,为了加速 level 0 到 level 1 的 compaction,我们会尽量保证level 0 和 level 1 有相同的 size。

当决定了 level 1 的大概 size,我们就需要决定 level multiplier。假设 level 1 的 size 是 512MB,level multiplier 是 10,整个 DB 的 size 是 500GB。Level 2 的 size 是 5GB,level 3 是 51GB,level 4 是 512GB,level 5 以及更上层的 level 就是空的。

那么 size amplification 就很容易计算了 (512 MB + 512 MB + 5GB + 51GB + 512GB) / (500GB) = 1.14,write amplification 的计算则是:任何一个 byte 首先写入 level 0,然后 compact 到 level 1,因为 level 1 的 size 跟 level 0 是一样的,所以 write amplification 在 level 0 到 level 1 的 compaction 是 2。当这个 byte compact 到 level 2 的时候,因为 level 2 比 level 1 大 10 倍,所以 write amplification 是 10。对于 level 2 到 level 3,level 3 到 level 4 也是一样。
所以总的 write amplification 就是 1 + 2 + 10 + 10 + 10 = 33。对于点查来说,通常会访问所有的 level 0 文件或者其他 level 的至多一个文件,这里我们可以使用 bloom filter 来减少 read amplification,但这个对于 range scans(也就是 iterator seek 这些)没啥作用,所以 range scans 的 read amplification 是 level 0 的文件数据 + 非空 level 的数量。

理解了上面的 level compaction 的流程,我们就可以开始配置相关的参数了。

  • level0_file_num_compaction_trigger:当 level 0 的文件数据达到这个值的时候,就开始进行 level 0 到 level 1 的 compaction。所以通常 level 0 的大小就是 write_buffer_size * min_write_buffer_number_to_merge * level0_file_num_compaction_trigger。
  • max_bytes_for_level_base 和 max_bytes_for_level_multiplier:max_bytes_for_level_base 就是 level1 的总大小,在上面提到,我们通常建议 level 1 跟 level 0 的 size 相当。上层的 level 的 size 每层都会比当前层大 max_bytes_for_level_multiplier 倍,这个值默认是 10,通常也不建议修改。
  • target_file_size_base 和 target_file_size_multiplier:target_file_size_base 则是 level 1 SST 文件的 size。上面层的文件 size 都会比当前层大 target_file_size_multiplier 倍,默认 target_file_size_multiplier 是 1,也就是每层的 SST 文件都是一样的。增加 target_file_size_base 会减少整个 DB 的 size,这通常是一件好事情,也通常建议 target_file_size_base 等于 max_bytes_for_level_base / 10,也就是 level 1 会有 10 个 SST 文件。
  • compression_per_level:使用这个来设置不同 level 的压缩级别,通常 level 0 和 level 1 不压缩,更上层压缩。也可以对更上层的选择慢的压缩算法,这样压缩效率更高,而对下层的选择快的压缩算法。
  • compression_per_level:使用这个来设置不同 level 的压缩级别,通常 level 0 和 level 1 不压缩,更上层压缩。也可以对更上层的选择慢的压缩算法,这样压缩效率更高,而对下层的选择快的压缩算法。TiKV 默认全选择的 lz4 的压缩方式。
Apache Flink 是一个分布式流处理框架,其每秒事务处理能力(TPS)是衡量系统性能的重要指标之一。在实际应用中,TPS 受多种因素影响,包括但不限于数据源的吞吐量、状态后端的选择、网络延迟、硬件资源以及配置参数等。 在某些场景下,使用分布式文件系统(DFS)作为主存储方案会导致显著的性能下降。测试表明,在未进行化的情况下,与本地磁盘(如 RocksDB)相比,基于 DFS 的去中心化状态存储可能会导致超过 95% 的 TPS 下降 [^1]。这主要是由于 DFS 在读写速度和延迟方面通常不如本地存储高效,特别是在高并发环境下,这种差异会更加明显。 为了提升 Flink 应用序的 TPS,可以采取以下几种化措施: ### 状态后端化 选择合适的状态后端对于提高 TPS 至关重要。RocksDB 是一种常用的嵌入式键值存储库,适用于大规模数据集的持久化存储。然而,当数据集较小且需要极低延迟时,使用内存型状态后端(如 FsStateBackend)可能更为合适。此外,还可以通过RocksDB 的配置参数化其性能,例如增大写缓冲区大小、减少压缩频率等。 ### 并行度设置 合理设置并行任务的数量有助于充分利用集群资源,从而提高整体吞吐量。可以通过 `-p` 参数指定作业的并行度,确保每个 TaskManager 拥有足够的任务槽位以支持所需的任务并行执行 [^2]。 ### 内存管理 Flink 提供了多个配置项用于控制 JobManager 和 TaskManager 的内存使用情况。适当增加 `jobmanager.memory.process.size` 和 `taskmanager.memory.process.size` 的值可以帮助缓解内存瓶颈问题,进而改善 TPS 表现 [^2]。 ### 网络与序列化化数据在网络中的传输效率也是提升 TPS 的关键步骤之一。可以通过启用批处理模式、整缓冲区大小以及采用高效的序列化/反序列化机制等方式实现这一目标。 ### 资源度与隔离 利用 YARN 或 Kubernetes 等资源度器提供的高级特性,如队列管理和先级划分,可以有效避免不同应用序之间的资源争抢现象,保证关键业务流获得足够的计算资源 [^2]。 ### 示例命令 下面是一个典型的提交 Flink 作业的命令示例,其中包含了部分性能相关的参数: ```bash bin/flink run \ -t yarn-per-job \ -d \ -p 5 \ -Drest.flamegraph.enabled=true \ -Dyarn.application.queue=test \ -Djobmanager.memory.process.size=1024mb \ -Dtaskmanager.memory.process.size=2048mb \ -Dtaskmanager.numberOfTaskSlots=2 \ -c com.bigdata.flink.tuning.SqlDemo \ /opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar \ --demo dim-difcount-filter ``` 以上方法不仅能够帮助开发者更好地理解和分析 Flink 应用序的运行状况,而且还能指导他们如何有效地进行性能,最终达到提高 TPS 的目的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值