为什么要Compaction
Doris 采用了类似于 LSM-Tree 的数据写入模型,将数据以追加的方式顺序写入磁盘,实现了写优化。这样可以提高系统的写入性能,但是也带来了读取时的额外开销。为了处理多次写入造成的数据变化,Doris 在读取时需要通过 Merge-on-Read 的策略,对不同版本的数据进行合并。
Merge-on-Read 会影响读取的效率,为了降低读取时需要合并的数据量,基于 LSM-Tree 的系统都会引入后台数据合并的逻辑,以一定策略定期的对数据进行合并。Doris 中这种机制被为 Compaction。
Doris 的 Compaction 操作针对单个的 Tablet,目的是减少 Tablet 中 Rowset 的数量。Compaction 操作在每个 BE 上独立运行,处理该 BE 节点上所有的数据分片。Compaction 是一个生产者-消费者模型,是一个 IO 密集型和内存密集型的任务。它本质上是一个多路归并排序的过程,每一路对应一个数据版本。
什么是LSM-Tree?
LSM-Tree 是 Log Structured Merge Tree 的缩写,它是一种分层、有序、面向磁盘的数据结构,它的设计原则是充分利用磁盘顺序写的性能优势,避免随机写的性能劣势。因此,它采用了类似于日志文件的追加写入方式,不涉及删除和修改操作。这样就大大提高了数据的写入效率,但同时也牺牲了一定的读取效率。所以,这种结构更适合于写多读少的场景。
在 LSM-Tree 中,最核心的数据结构是 SSTable(Sorted String Table),这个概念源自于 Google 的 Bigtable 论文。SSTable 是一种持久化、有序且不可变的键值存储结构,它可以存储任意长度的 key 和 value,并且支持按指定 key 查找和按指定范围迭代遍历 key 的功能。
本质上,Doris 的数据存储就是在类似 SSTable(Sorted String Table)的数据结构中。
缺点
用户可能需要根据实际的使用场景来调整 Compaction 的策略,否则可能遇到如下问题:
一、Compaction 速度低于数据写入速度
在高频写入场景下,短时间内会产生大量的数据版本。如果 Compaction 不及时,就会造成大量版本堆积,最终严重影响写入速度。
一个非常典型的问题是:tablet writer write failed, err=-235
问题,这个问题的原因就是tablet未合并的版本个数过多,超过默认上限(max_tablet_version_num=500
)。
二、写放大问题
Compaction 的本质是把已经写入的数据重新读取并写回,这样会导致数据被多次写入,这种现象叫做写放大。一个优秀的 Compaction 策略应该在保证效率的同时,尽可能地降低写放大系数。过度的 Compaction 会消耗大量的磁盘 IO 资源,影响系统的整体性能。
Compaction的类型
Base Compaction
Base Compaction 是将基线数据版本(最早的数据版本)和后来导入的数据版本合并成一个大的数据版本的过程。这样可以减少数据版本的数量,提高查询效率,但是也会消耗很多磁盘空间和IO资源。
Cumulative Compaction
如果只有 Base Compaction,则每次增量数据都要和全量的基线数据合并,写放大问题会非常严重,并且每次 Compaction 都相当耗时。因此引入 Cumulative Compaction 来先对增量数据进行合并,Cumulative Compaction 将多个最新导入的数据版本合并成一个较大的数据版本的过程。这样可以减少最新数据版本之间的重复数据,降低写放大系数,但是也会增加查询时需要合并的数据量。
Base Compaction 和 Cumulative Compaction 的边界由 Cumulative Point 来确定,Cumulative Point 是一个根据数据分布和变化情况动态调整的参数。
Vertical compaction
Vertical compaction 是 Doris 1.2.2 版本引入的一种新的 Compaction 算法,它改变了原来按行合并的方式,而是按列组进行合并,这样每次合并的数据粒度就变成了列组,从而减少了单次 compaction 参与的数据量和内存占用。Vertical compaction 针对大宽表场景下的 Compaction 性能和资源消耗问题进行了优化,可以有效地降低 Compaction 的内存开销,并提升 Compaction 的执行速度。
Segment compaction
Segment compaction 主要是为了解决一次导入很多数据的场景。它和 Vertical compaction 的触发时机不一样,它是在导入数据的时候,把一批数据里面的多个 Segment 合并成一个或几个 Segment。这样做可以有效地减少最后生成的 Segment 的数量,避免出现 -238
(OLAP_ERR_TOO_MANY_SEGMENTS
)错误,这个错误表示 Segment 的数量太多了。
Segment compaction和Vertical compaction都是Doris中的Compaction算法,它们的目的都是为了减少数据的冗余和提高查询效率。但是它们的触发机制和合并方式不同。Segment compaction是在导入数据的时候,针对一批数据内,多个 Segment 进行的合并操作;而Vertical compaction是在导入完成后,针对一个或多个版本内,多个 Rowset 进行的合并操作。Segment compaction可以减少最终生成的Segment数量,避免出现Segment过多的错误;而Vertical compaction可以减少最终生成的Rowset数量,避免出现Rowset过多的错误。
总结
由于Doris采用了类似于 LSM-Tree 的数据写入模型,在读取的时候需要对数据进行聚合,也就是Merge-on-Read。为了解决聚合带来的性能问题,Doris实现了多种Compaction机制对数据写入后进行聚合,以提高读性能。
参考资料
- https://cloud.tencent.com/developer/article/1441835
- https://doris.apache.org/zh-CN/docs/dev/advanced/best-practice/compaction
- https://mp.weixin.qq.com/s/H2e0Ww-tmqOndfMpu8Q2XA
公众号:大数据小屋
快速复制:
大数据小屋
二维码: