广州网站设计平台,贵阳网站开发哪家专业,微商城是什么意思,wordpress 新建表插件截至当前#xff0c;Flink 作业的状态后端仍然只有 Memory、FileSystem 和 RocksDB 三种可选#xff0c;且 RocksDB 是状态数据量较大#xff08;GB 到 TB 级别#xff09;时的唯一选择。RocksDB 的性能发挥非常仰赖调优#xff0c;如果全部采用默认配置#xff0c;读写性…截至当前Flink 作业的状态后端仍然只有 Memory、FileSystem 和 RocksDB 三种可选且 RocksDB 是状态数据量较大GB 到 TB 级别时的唯一选择。RocksDB 的性能发挥非常仰赖调优如果全部采用默认配置读写性能有可能会很差。
但是RocksDB 的配置也是极为复杂的可调整的参数多达百个没有放之四海而皆准的优化方案。如果仅考虑 Flink 状态存储这一方面我们仍然可以总结出一些相对普适的优化思路。本文先介绍一些基础知识再列举方法。
Note本文的内容是基于我们在线上运行的 Flink 1.9 版本实践得出的。在1.10版本及以后由于 TaskManager 内存模型重构RocksDB 内存默认成为了堆外托管内存的一部分可以免去一些手动调整的麻烦。如果性能仍然不佳需要干预则必须将 state.backend.rocksdb.memory.managed 参数设为 false 来禁用 RocksDB 内存托管。 Stae R/W on RocksDB RocksDB 作为 Flink 状态后端时的读写逻辑与一般情况略有不同如下图所示。 Flink 作业中的每一个注册的状态都对应一个列族column family即包含自己独立的 memtable 和 sstable 集合。写操作会先将数据写入活动 memtable写满之后则会转换为不可变 memtable并 flush 到磁盘中形成 sstable。读操作则会依次在活动 memtable、不可变 memtable、block cache 和 sstable 中寻找目标数据。另外sstable 也需要通过 compaction 策略进行合并最终形成分层的 LSM Tree 存储结构老生常谈了。 特别地由于 Flink 在每个检查点周期都会将 RocksDB 的数据快照持久化到文件系统所以自然也就不需要再写预写日志WAL了可以安全地关闭WAL与fsync。
之前笔者已经详细讲解过 RocksDB compaction 策略并且提到了读放大、写放大和空间放大的概念对 RocksDB 的调优本质上就是在这三个因子之间取得平衡。而在 Flink 作业这种注重实时性的场合则要重点考虑读放大和写放大。 Tuning MemTable memtable 作为 LSM Tree 体系里的读写缓存对写性能有较大的影响。以下是一些值得注意的参数。为方便对比下文都会将 RocksDB 的原始参数名与 Flink 配置中的参数名一并列出用竖线分割。 write_buffer_size | state.backend.rocksdb.writebuffer.size 单个 memtable 的大小默认是64MB。当 memtable 大小达到此阈值时就会被标记为不可变。一般来讲适当增大这个参数可以减小写放大带来的影响但同时会增大 flush 后 L0、L1 层的压力所以还需要配合修改 compaction 参数后面再提。 max_write_buffer_number | state.backend.rocksdb.writebuffer.count memtable 的最大数量包含活跃的和不可变默认是2。当全部 memtable 都写满但是 flush 速度较慢时就会造成写停顿所以如果内存充足或者使用的是机械硬盘建议适当调大这个参数如4。 min_write_buffer_number_to_merge | state.backend.rocksdb.writebuffer.number-to-merge 在 flush 发生之前被合并的 memtable 最小数量默认是1。举个例子如果此参数设为2那么当有至少两个不可变 memtable 时才有可能触发 flush亦即如果只有一个不可变 memtable就会等待。调大这个值的好处是可以使更多的更改在 flush 前就被合并降低写放大但同时又可能增加读放大因为读取数据时要检查的 memtable 变多了。经测试该参数设为2或3相对较好。
Tuning Block/Block Cache
block 是 sstable 的基本存储单位。block cache 则扮演读缓存的角色采用 LRU 算法存储最近使用的 block对读性能有较大的影响。 block_size | state.backend.rocksdb.block.blocksize block 的大小默认值为4KB。在生产环境中总是会适当调大一些一般32KB比较合适对于机械硬盘可以再增大到128~256KB充分利用其顺序读取能力。但是需要注意如果 block 大小增大而 block cache 大小不变那么缓存的 block 数量会减少无形中会增加读放大。 block_cache_size | state.backend.rocksdb.block.cache-size block cache 的大小默认为8MB。由上文所述的读写流程可知较大的 block cache 可以有效避免热数据的读请求落到 sstable 上所以若内存余量充足建议设置到128MB甚至256MB读性能会有非常明显的提升。 Tuning Compaction
compaction 在所有基于 LSM Tree 的存储引擎中都是开销最大的操作弄不好的话会非常容易阻塞读写。建议看官先读读前面那篇关于 RocksDB compaction 策略的文章获取一些背景知识这里不再赘述。 compaction_style | state.backend.rocksdb.compaction.style compaction 算法使用默认的 LEVEL即 leveled compaction即可下面的参数也是基于此。 target_file_size_base | state.backend.rocksdb.compaction.level.target-file-size-baseL1层单个 sstable 文件的大小阈值默认值为64MB。每向上提升一级阈值会乘以因子 target_file_size_multiplier但默认为1即每级sstable最大都是相同的。显然增大此值可以降低 compaction 的频率减少写放大但是也会造成旧数据无法及时清理从而增加读放大。此参数不太容易调整一般不建议设为256MB以上。 max_bytes_for_level_base | state.backend.rocksdb.compaction.level.max-size-level-base L1层的数据总大小阈值默认值为256MB。每向上提升一级阈值会乘以因子 max_bytes_for_level_multiplier默认值为10。由于上层的大小阈值都是以它为基础推算出来的所以要小心调整。建议设为 target_file_size_base 的倍数且不能太小例如5~10倍。 level_compaction_dynamic_level_bytes | state.backend.rocksdb.compaction.level.use-dynamic-size 这个参数之前讲过。当开启之后上述阈值的乘法因子会变成除法因子能够动态调整每层的数据量阈值使得较多的数据可以落在最高一层能够减少空间放大整个 LSM Tree 的结构也会更稳定。对于机械硬盘的环境强烈建议开启。 Generic Parameters max_open_files | state.backend.rocksdb.files.open 顾名思义是 RocksDB 实例能够打开的最大文件数默认为-1表示不限制。由于sstable的索引和布隆过滤器默认都会驻留内存并占用文件描述符所以如果此值太小索引和布隆过滤器无法正常加载就会严重拖累读取性能。 max_background_compactions/max_background_flushes | state.backend.rocksdb.thread.num 后台负责 flush 和 compaction 的最大并发线程数默认为1。注意 Flink 将这两个参数合二为一处理对应 DBOptions.setIncreaseParallelism() 方法鉴于 flush 和 compaction 都是相对重的操作如果 CPU 余量比较充足建议调大在我们的实践中一般设为4。 除了上述设置参数的方法之外用户还可以通过实现 ConfigurableRocksDBOptionsFactory 接口创建 DBOptions 和 ColumnFamilyOptions 实例来传入自定义参数更加灵活一些。看官可参考 Flink 预先定义好的几个 RocksDB 参数集位于 PredefinedOptions 枚举中获取更多信息。 RocksDB使用场景和特性 存储和访问数百PB的数据是一个非常大的挑战开源的RocksDB就是FaceBook开放的一种嵌入式、持久化存储、KV型且非常适用于fast storage的存储引擎。 传统的数据访问都是RPC但是这样的话访问速度会很慢不适用于面向用户的实时访问的场景。随着fast storage的流行越来越多的应用可以通过在flash中管理数据并快速直接的访问数据。这些应用就需要使用到一种嵌入式的database。 使用嵌入式的database的原因有很多。当数据请求频繁访问内存或者fast storage时网路延时会增加响应时间比如访问数据中心网络耗时可能就耗费50ms跟访问数据的耗时一样多甚至更多。这意味着通过RPC访问数据有可能是本地直接访问耗时的两倍。另外机器的core数越来越多storage-IOPS的访问频率也达到了每秒百万次传统数据库的锁竞争和context 切换会成为提高storage-IOPS的瓶颈。所以需要一种容易扩展和针对未来硬件趋势可以定制化的databaseRocksDB就是一种选择。 RocksDB是基于Google的开源key value存储库LevelDB主要满足以下目标
1、适用于多cpu场景 商业服务器一般会有很多cpu核要开发一个随着CPU 核数吞吐量也随之增大的数据库是很困难的更别提是线性的递增关系。但是RocksDB是可以高效地运行在多核服务器上。一个优点是RocksDB提供的语义比传统的DBMS更简单。例如RocksDB支持MVCC但是仅限于只读的transaction。另一个优点是数据库在逻辑上分片为read-only path和read-write path。这两种方法可以降低锁竞争而降低锁竞争是支持高并发负载的前提条件。
2、高校利用storage(更高的IOPS、高效的压缩、更少的写磨损) 现在的存储设备都可以支持到每秒10w的随机读如果有10块存储卡的话就可以支持每秒100w的随机读。RocksDB可以在这种快速存储上高效运行且不会成为性能瓶颈。 和实时更新的B-tree相比RocksDB有更好的压缩和更小的写放大。RocksDB由于压缩更优所以占用更少的storage由于更小的写放大flash 设备可以更持久。
3、弹性架构支持扩展 RocksDB支持扩展。比如我们可以新增一个merge operator这样就可以使用write-only来替代read-modify-write。然而read和Write是会增加存储的读写IOPS。在写频繁的负载下这种措施可以降低IOPS。
4、支持IO-bound、in-memory、write-once IO-bound workload是指数据库大小远大于内存且频繁地访问storage。in-memory workload是指数据库数据都在内存中且仍然使用storage来持久化存储DB。write-once workload是指大部分的key都只会写入一次或者insert且没有更新操作。现在RocksDB很好支持IO-bound要想更好地支持in-memory需要做一些工作。支持write-once的话还有很多遗留问题待解决。 RocksDB不是一个分布式的DB而是一个高效、高性能、单点的数据库引擎。RocksDB是一个持久化存储keys和values的c library。keys 和values可以是任意的字节流且按照keys有序存储。后台的compaction会消除重复的和已删除的key。RocksDB的data以log-structured merge tree的形式存储。RocksDB支持原子的批量写入操作以及前向和后向遍历。 RocksDB采用“可插拔式”的架构所以很容易替换其中的组件允许用户很容易在不同的负载和硬件设备上进行调优。 1441593_10151976018697200_1580274673_n.png 比如用户可以添加不同的压缩模块(snappy, zlib, bzip, etc)且使用不同模块时不用修改源码。这可用于在不同负载下通过配置使用不同的压缩算法。同理用户可以在compaction时加载个性化的compaction filter来处理keys例如可以实现DB的key的expire-time功能。RocksDB有可插拔式的API所以应用可以设计个性化的数据结构来cache DB的写数据典型应用就是prefix-hash其中一部分key使用hash存储剩下的key存储在B-tree。storage file的实现也可以定制开发所以用户可以实现自己的storage file格式。 RocksDB支持两种compaction stylelevel style和universal style。这两种style可做读放大、写放大、空间放大之间做tradeoff。compaction也支持多线程所以打的DB可以支持高性能的compaction。 RocksDB也提供在线的增量备份接口也支持bloom filters这可以在range-scan时降低IOPS。 RocksDB可以充分挖掘使用flash的IOPS在随机读、随机写和bulk load时性能优于LevelDB。在随机写和bulk load时性能优于LevelDB 10倍在随机读时性能优于LevelDB 30%。 LevelDB是单线程执行compaction在特定的server workload下表现堪忧但是RocksDB在IO-bound workload下性能明显优于LevelDB。在测试中发现LevelDB发生频繁的write-stall这严重影响了DB的99%延迟另外也发现把文件mmap到OS cache会引入读性能瓶颈。测试表明应用不能充分使用flash的高性能这是因为数据的带宽瓶颈引起了LevelDB的写放大。通过提高写速率和降低写放大可以避免很多问题同时提高RocksDB性能。 RocksDB的典型场景低延时访问: 1、需要存储用户的查阅历史记录和网站用户的应用 2、需要快速访问数据的垃圾检测应用 3、需要实时scan数据集的图搜索query 4、需要实时请求Hadoop的应用 5、支持大量写和删除操作的消息队列