1背景
在「Hummock :专为 RisingWave 流处理而设计的云原生存储引擎」一文中里我们已经介绍了 Hummock 的设计理念与基本架构,本文主要介绍 Hummock 近期几个版本的重大改进与优化
2Fast Compaction
在设计之初,Hummock 的数据文件格式参考了 RocksDB Sstable 的设计。之后为了提升查询性能,Hummock 引入了 Xor Filter 用于替换原有的 Bloom Filter (RocksDB 也引入了基于 Xor Filter 改造的 Ribbon Filter 算法)。但是 Xor Filter 在构建过程中会短暂使用大量的内存,为了减少内存的使用,Hummock 改造了数据存储格式,转而为每个 block 单元的数据构建 Filter 而不是构建全局的单一 Filter,从而降低了构建过程中的内存峰值,使得 RisingWave 可以运行在更加低配的环境中。
LSM Tree 相比 BTree 类的数据结构而言,通过顺序写入+后台整理的方式换来了更高的写入吞吐,然而也付出写放大的代价的,也就是说同一份数据可能会被反复整理(compact),更多细节参考 (https://github.com/facebook/rocksdb/wiki/RocksDB-Tuning-Guide) 。对于 L1 及更旧的数据而言,当数据库试图把上层的一个文件的数据挪到下层时,往往会和下层的多个文件产生重合,为此不得不把远多于一个文件引入同一个 compact 任务中,这正是写放大的主要来源之一。我们注意到,即便上层的文件和下层的多个文件产生了范围重叠,依然不意味着上层的每一个 block (通常在 4KB ~ 256KB 之间)都和下层的某个 block 产生了范围重叠,除非每次写入的数据都在同一个范围内严格的等概率分布。因此,如果我们的 compact 算法不是按照 key 为粒度来遍历数据,而是以 block 为粒度,那么对于那些没有和任务中其他文件的 block 产生重叠的 block,可以避免解压以及再次压缩的开销,直接将这部分数据复制到新的文件中。
传统的 compact 算法如下图所示:
而改进后的算法如下图所示:
通过对比可以看出,SST2 中的 block-1 和 block-3 如果和 SST1 中的数据没有重复的话,可以直接复制到输出文件中,避免额外的解析以及压缩开销
我们测试了多种不同的读写负载,该优化能够节约20%~50%的 compactor CPU 资源。对于那些写入数据较多、吞吐较大的场景,该优化能够显著降低 compact 操作带来的成本问题。目前该功能还在测试中,默认不开启。
3IO Prefetch
由于 Hummock 的存储设计和 RocksDB 一样,是基于 Block 为粒度来组织数据。当上层应用查询数据时,会逐个读取 block ,如果 block 不在 cache 中,则触发一次 IO 操作读取 remote storage (如果是 AWS 环境部署,则后端为 S3 存储)。然而对 S3 有所了解的开发者都知道,S3 的随机读延迟极高,更适合用于大对象的顺序读取。因此,当用户执行 OLAP 类型的查询时(例如:select count(*) from mv; ),Hummock 需要逐一发送 IO 操作,极大地增加了查询耗时。还有一种类似的场景,对于已经导入的数据表,用户希望再次创建出另外的 Materialize View, 此时也需要遍历所有的历史数据,因此也会受制于上面所提到的读取方式。
因此,对于 RisingWave 中的 batch 查询,我们在底层的存储中,当发现 cache 中没有当前 block 时,会一次性读取当前范围内的多个 block。通过这样的方式我们能够尽可能最大限度地利用用满 S3 的带宽吞吐,同时由于 S3 按照 IO 个数收费,而流式接口只算做一次 IO,因此该优化也同时降低了 S3 的 IO 成本。通过一些简单的测试,该优化可以提升部分 OLAP 查询速度至多 8 倍。当然这只是极端情况下,还有很多复杂的 SQL 瓶颈在于计算操作,实际提升还取决于查询类型与业务场景。
4总结
除了查询性能以外,成本也是数据库所关注的重点。对于 AWS S3 这样的存储后端来说,除了存储数据按照大小每月固定收费以外,IO 操作按照次数收费。因此,除了优化性能以外,如何利用 S3 的高吞吐高延迟的特征,尽可能提高 S3 的使用效率来降低成本,也是我们优化存储系统的重要方向之一。接下来我们会根据查询计划的不同更加精确地选择 IO 策略来进一步提高处理速度,以及基于本地磁盘作为二级缓存来进一步提高命中率,减少对 S3 的访问频率。
RisingWave 是一款基于 Apache 2.0 协议开源的分布式流数据库,致力于为用户提供极致简单、高效的流数据处理与管理能力。RisingWave 采用存算分离架构,实现了高效的复杂查询、瞬时动态扩缩容以及快速故障恢复,并助力用户极大地简化流计算架构,轻松搭建稳定且高效的流计算应用。RisingWave 始终聆听来自社区的声音,并积极回应用户的反馈。目前,RisingWave 已汇聚了近 150 名开源贡献者和近 3000 名社区成员。全球范围内,已有上百个 RisingWave 集群在生产环境中部署。
了解更多:
官网: risingwave.com
教程:risingwavetutorial.com
GitHub:risingwave.com/github
微信公众号:RisingWave中文开源社区
社区用户交流群:risingwave_assistant