布景

LSM-Tree( Log Structured-Merge Tree)是数据库中最为常见的存储结构之一,其核心思想在于充分发挥磁盘接连读写的功用优势、以短时间的内存与 IO 的开支换取最大的写入功用,数据以 Append-only 的方法写入 Memtable、达到阈值后冻住 Memtable 并 Flush 为磁盘文件、再结合 Compaction 机制将多个小文件进行多路归并排序构成新的文件,终究完结数据的高效写入。

Apache Doris 的存储模型也是选用类似的 LSM-Tree 数据模型。用户不同批次导入的数据会先写入内存结构,随后在磁盘上构成一个个的 Rowset 文件,每个 Rowset 文件对应一次数据导入版别。而 Doris 的 Compaction 则是负责将这些 Rowset 文件进行兼并,将多个 Rowset 小文件兼并成一个 Rowset 大文件。

资源消耗降低 90%,速度提升 50%,解读 Apache Doris Compaction 最新优化与实现

在此进程中 Compaction 发挥着以下作用:

  • 每个 Rowset 内的数据是按主键有序的,但 Rowset 与 Rowset 之间数据是无序的,Compaction 会将多个 Rowset 的数据从无序变为有序,提高数据在读取时的功率;
  • 数据以 Append-only 的方法进行写入,因此 Delete、Update 等操作都是标记写入,Compaction 会将标记的数据进行真实删除或更新,防止数据在读取时进行额外的扫描及过滤;
  • 在 Aggregate 模型上,Compaction 还可以将不同 Rowset 中相同 Key 的数据进行预聚合,削减数据读取时的聚合核算,进一步提高读取功率。

问题与考虑

尽管 Compaction 在写入和查询功用方面发挥着非常要害的作用,但 Compaction 使命履行期间的写扩大问题以及随之而来的磁盘 I/O 和 CPU 资源开支,也为体系安稳性和功用的充分发挥带来了新的应战。

在用户真实场景中,往往面临着林林总总的数据写入需求,并行写入使命的多少、单次提交数据量的巨细、提交频次的高低等,各种场景或许需求调配不同的 Compaction 战略。而不合理的 Compaction 战略则会带来一系列问题:

  • Compaction 使命调度不及时导致很多版别堆积、Compaction Score 过高,终究导致写入失利(-235/-238);
  • Compaction 使命履行速度慢,CPU 耗费高;
  • Compaction 使命内存占用高,影响查询功用甚至导致 BE OOM;

与此一起,尽管 Apache Doris 提供了多个参数供用户进行调整,但相关参数众多且语义杂乱,用户理解本钱过高,也为人工调优添加了难度。

根据以上问题,从 Apache Doris 1.1.0 版别开端,咱们添加了自动触发式 QuickCompaction、引入了 Cumulative Compaction 使命的隔离调度并添加了小文件兼并的梯度兼并战略,对高并发写入和数据实时可见等场景都进行了针对性优化

而在 Apache Doris 最新的 1.2.2 版别和行将发布的 2.0.0 版别中,咱们对体系 Compaction 才能进行了全方位增强,在触发战略、履行 方法 工程完结 以及参数装备上都进行了大幅优化, 在实时性、易用性与安稳性得到提高的一起更是彻底解决了查询功率问题

Compaction 优化与完结

在规划和评价 Compaction 战略之时,咱们需求归纳权衡 Compaction 的使命模型和用户真实运用场景,核心优化思路包含以下几点:

  • 实时性和高效性。Compaction 使命触发战略的实时性和使命履行方法的高效性直接影响到了查询履行的速度,版别堆积将导致 Compaction Score 过高且触发自我维护机制,导致后续数据写入失利。
  • 安稳性。Compaction 使命对体系资源的耗费可控,不会因 Compaction 使命带来过多的内存与 CPU 开支构成体系不安稳。
  • 易用性。因为 Compaction 使命涉及调度、战略、履行多个逻辑单元,部分特别场景需求对 Compaction 进行调优,因此需求 Compaction 涉及的参数可以精简明了,辅导用户快速进行场景化的调优。

详细在完结进程中,包含了触发战略、履行方法、工程完结以及参数装备这四个方面的优化。

资源消耗降低 90%,速度提升 50%,解读 Apache Doris Compaction 最新优化与实现

Compaction 触发战略

调度战略决议着 Compaction 使命的实时性。在 Apache Doris 2.0.0 版别中,咱们在自动触发和被迫扫描这两种方法的根底之上引入了 Tablet 休眠机制,力求在各类场景均能以最低的耗费保证最高的实时性。

自动触发

自动触发是一种最为实时的方法,在数据导入的阶段就检查 Tablet 是否有待触发的 Compaction 使命,这样的方法保证了 Compaction 使命与数据导入使命同步进行,在新版别发生的一起就可以立即触发数据兼并,可以让 Tablet 版别数维持在一个非常安稳的状况。自动触发首要针对增量数据的 Compaction (Cumulative Compaction),存量数据则依赖被迫扫描完结。

被迫扫描

与自动触发不同,被迫扫描首要负责触发大数据量的 Base Compaction 使命。Doris 通过启动一个后台线程,对该节点上一切的 Tablet 元数据进行扫描,根据 Tablet Compaction 使命的紧迫程度进行打分,挑选得分最高的 Tablet 触发 Compaction 使命。这样的全局扫描形式可以选出最紧迫的 Tablet 进行 Compaction,但一般其履行周期较长,所以需求配合自动触发战略施行。

休眠机制

频频的元信息扫描会导致很多的 CPU 资源浪费。因此在 Doris 2.0.0 版别中咱们引入了 Tablet 休眠机制,来下降元数据扫描带来的 CPU 开支。通过对长期没有 Compaction 使命的 Tablet 设置休眠时间,一段时间内不再对该 Tablet 进行扫描,可以大幅下降使命扫描的压力。一起假如休眠的 Tablet 有突发的导入,通过自动触发的方法也能顾唤醒 Compaction 使命,不会对使命的实时性有任何影响。

通过上述的自动扫描+被迫触发+休眠机制,运用最小的资源耗费,保证了 Compaction 使命触发的实时性。

Compaction 履行方法

在 Doris 1.2.2 版别中中,咱们引入了两种全新的 Compaction 履行方法:

  • Vertical Compaction,用以彻底解决 Compaction 的内存问题以及大宽表场景下的数据兼并;
  • Segment Compaction,用以彻底解决上传进程中的 Segment 文件过多问题;

而在行将发布的 Doris 2.0.0 版别,咱们引入了 Ordered Data Compaction 以提高时序数据场景的数据兼并才能。

Vertical Compaction

在之前的版别中,Compaction 一般选用行的方法进行,每次兼并的根本单元为整行数据。因为存储引擎选用列式存储,行 Compaction 的方法对数据读取极端不友爱,每次 Compaction 都需求加载一切列的数据,内存耗费极大,而这样的方法在宽表场景下也将带来内存的极大耗费。

针对上述问题,咱们在 Doris 1.2.2 版别中完结了对列式存储愈加友爱的 Vertical Compaction,详细履行流程如下图:

资源消耗降低 90%,速度提升 50%,解读 Apache Doris Compaction 最新优化与实现

整体分为如下几个步骤:

  1. 切排列组。将输入 Rowset 依照列进行切分,一切的 Key 列一组、Value 列按 N 个一组,切分成多个 Column Group;
  2. Key 列兼并。Key 列的次序就是终究数据的次序,多个 Rowset 的 Key 列选用堆排序进行兼并,发生终究有序的 Key 列数据。在发生 Key 列数据的一起,会一起发生用于标记全局序 RowSources。
  3. Value 列的兼并。逐一兼并 Column Group 中的 Value 列,以 Key 列兼并时发生的 RowSources 为依据对数据进行排序。
  4. 数据写入。数据按列写入,构成终究的 Rowset 文件。

因为选用了按列组的方法进行数据兼并,Vertical Compaction 天然与列式存储愈加贴合,运用列组的方法进行数据兼并,单次兼并只需求加载部排列的数据,因此可以极大削减兼并进程中的内存占用。在实践测验中,Vertical C ompaction 运用内存仅为原有 Compaction 算法的 1/10,一起 Compaction 速率提高 15%。

Vertical Compaction 在 1.2.2 版别中默认封闭状况,需求在 BE 装备项中设置 enable_vertical_compaction=true 敞开该功用。

相关PR:github.com/apache/dori…

Segment Compaction

在数据导入阶段,Doris 会在内存中积攒数据,到达必定巨细时 Flush 到磁盘构成一个个的 Segment 文件。大批量数据导入时会构成很多的 Segment 文件从而影响后续查询功用,根据此 Doris 对一次导入的 Segment 文件数量做了约束。当用户导入很多数据时,或许会触发这个约束,此时体系将反馈 -238 (TOO_MANY_SEGMENTS) 一起停止对应的导入使命。Segment compaction 允许咱们在导入数据的一起进行数据的实时兼并,以有效控制 Segment 文件的数量,添加体系所能承载的导入数据量,一起优化后续查询功率。详细流程如下所示:

资源消耗降低 90%,速度提升 50%,解读 Apache Doris Compaction 最新优化与实现

在新增的 Segment 数量超过必定阈值(例如 10)时即触发该使命履行,由专门的兼并线程异步履行。通过将每组 10个 Segment 兼并成一个新的 Segment 并删除旧 Segment,导入完结后的实践 Segment 文件数量将下降 10 倍。Segment Compaction 会随同导入的进程并行履行,在大数据量导入的场景下,可以在不明显添加导入时间的前提下大幅下降文件个数,提高查询功率。

Segment Compaction 在 1.2.2 版别中默认封闭状况,需求在 BE 装备项中设置 enable_segcompaction = true 敞开该功用。

相关 PR : github.com/apache/dori…

Ordered Data Compaction

随着越来越多用户在时序数据分析场景应用 Apache Doris,咱们在 Apache Doris 2.0.0 版别完结了全新的 Ordered Data Compaction。

时序数据分析场景一般具有如下特色:数据整体有序、写入速率稳定、单次导入文件巨细相对平均。针对如上特色,Ordered Data Compaction 无需遍历数据,跳过了传统 Compaction 杂乱的读数据、排序、聚合、输出的流程,通过文件 Link 的方法直接操作底层文件生成 Compaction 的目标文件。

资源消耗降低 90%,速度提升 50%,解读 Apache Doris Compaction 最新优化与实现

Ordered Data Compaction 履行流程包含如下几个要害阶段:

  1. 数据上传阶段。记载 Rowset 文件的 Min/Max Key,用于后续兼并 Rowset 数据交叉性的判别;
  2. 数据检查阶段。检查参加 Compaction 的 Rowset 文件的有序性与整齐度,首要通过数据上传阶段的 Min /Max Key 以及文件巨细进行判别。
  3. 数据兼并阶段。将输入 Rowset 的文件硬链接到新 Rowset,然后构建新 Rowset 的元数据(包含行数,Size,Min/Max Key 等)。

可以看到上述阶段与传统的 Compaction 流程完全不一样,只需求文件的 Link 以及内存元信息的构建,极端简洁、轻量。针对时序场景规划的 Ordered Data Compaction 可以在毫秒级别完结大规模的 Compaction 使命,其内存耗费几乎为 ****0,对用户极端友爱。

Ordered Data Compaction 在 2.0.0 版别中默认敞开状况,如需调整在 BE 装备项中修改 enable_segcompaction 即可。

运用方法:BE 装备 enable_ordered_data_compaction=true

Compaction 工程完结

除了上述在触发战略和 Compaction 算法上的优化之外,Apache Doris 2.0.0 版别还对 Compaction 的工程完结进行了很多细节上的优化,包含数据零复制、按需加载、Idle Schedule 等。

数据零复制

Doris 选用分层的数据存储模型,数据在 BE 上可以分为如下几层:Tablet -> Rowset -> Segment -> Column -> Page,数据需求通过逐层处理。因为 Compaction 每次参加的数据量大,数据在各层之间的流转会带来很多的 CPU 耗费,在新版别中咱们规划并完结了全流程无复制的 Compaction 逻辑,Block 从文件加载到内存中后,后续无序再进行复制,各个组件的运用都通过一个 BlockView 的数据结构完结,这样彻底的解决了数据逐层复制的问题,将 Compaction 的功率再次提高了 5%。

按需加载

Compaction 的逻辑本质上是要将多个无序的 Rowset 兼并成一个有序的 Rowset,在大部分场景中,Rowset 内或许 Rowset 间的数据并不是完全无序的,可以充分利用局部有序性进行数据兼并,在同一时间仅需加载有序文件中的第一个,这样随着兼并的进行再逐步加载。利用数据的局部有序性按需加载,可以极大削减数据兼并进程中的内存耗费。

Idle schedule

在实践运行进程中,因为部分 Compaction 使命占用资源多、耗时长,经常出现因为 Compaction 使命影响查询功用的 Case。这类 Compaction 使命一般存在于 Base compaction 中,具有数据量大、履行时间长、版别兼并少的特色,对使命履行的实时性要求不高。在新版别中,针对此类使命敞开了线程 Idle Schedule 特性,下降此类使命的履行优先级,防止 Compaction 使命构成线上查询的功用动摇。

易用性

在 Compaction 的易用性方面,Doris 2.0.0 版别进行了体系性优化。结合长期以来 Compaction 调优的一些经历数据,默认装备了一套通用环境下体现最优的参数,一起大幅精简了 Compaction 相关参数及语义,便利用户在特别场景下的 Compaction 调优。

资源消耗降低 90%,速度提升 50%,解读 Apache Doris Compaction 最新优化与实现

总结规划

通过上述一系列的优化方法, 全新版别在 Compaction 进程中取得了极为明显的改善作用。在 ClickBench 功用测验中,新版别 Compaction 履行速度 达到 30w row/s,相较于旧版别 提高 50 % ;资源耗费降幅巨大, 内存占用仅为原先的 10% 。高并发数据导入场景下,Compaction Score 一直保持在 50 左右,且体系体现极为平稳。一起在时序数据场景中,Compaction 写扩大系数下降 90%,极大提高了可承载的写入吞吐量。

后续咱们仍将进一步探究迭代优化的空间,首要的工作方向将聚焦在自动化、可观测性以及履行功率等方向上:

  1. 自动化调优。针对不同的用户场景,无需人工干预,体系支持进行自动化的 Compaction 调优;
  2. 可观测性增强。搜集计算 Compaction 使命的各项指标,用于辅导自动化以及手动调优;
  3. 并行 Vertical Compaction。通过 Value 列并发履行,进一步提高 Vertical Compaction 功率。

以上方向的工作都已处于规划或开发中,假如有小伙伴对以上方向感兴趣,也欢迎参加到社区中的开发来。期待有更多人参加到 Apache Doris 社区的建设中 ,欢迎你的参加!

作者介绍:

一休,Apache Doris contributor,SelectDB 资深研发工程师

张正宇,Apache Doris contributor,SelectDB 资深研发工程师

# 相关链接:

SelectDB 官网

selectdb.com

Apache Doris 官网

doris.apache.org

Apache Doris Github

github.com/apache/dori…