Doris作为大数据存储与处理领域一款重要的工具,其compaction(数据合并)操作对于系统性能至关重要。尤其是在数据量达到一定规模时,合理的compaction运维能够有效提升系统的读写效率。本文将深入探讨Doris compaction的运维方法。

一、背景与Compaction的定义

当前,Doris常被用作冷库,处理5亿数据的读写操作。在数据写入HDD(硬盘驱动器)时,性能会有所下降。为解决这一问题,需要借助compaction将小文件按照版本合并,把随机写转换为顺序写,从而提升性能。在此过程中,确保compaction score消耗低于100是关键。

Doris的数据写入采用类似LSM – Tree的数据结构,数据以追加(Append)的方式写入磁盘。这种结构能将随机写优化为顺序写,提升系统写入吞吐能力。然而,在读取数据时,由于多次写入的数据需要合并,采用Merge – on – Read方式会影响读取效率。为降低读取时的合并数据量,基于LSM – Tree的系统引入了后台数据合并逻辑,定期按照一定策略对数据进行合并,这就是Doris中的Compaction机制。

二、Compaction使用中遇到的问题

(一)Compaction速度低于数据写入速度

在高频写入的场景下,短时间内会生成大量数据版本。若Compaction不能及时进行,大量版本就会堆积,最终严重影响数据写入速度。这就好比一条生产线,生产速度过快,而整理打包的速度跟不上,导致货物堆积,影响整个生产流程。

(二)写放大问题

Compaction本质上是读取已写入的数据并重新写回的过程,这种数据重复写入被称为写放大。一个好的Compaction策略需要在保证效率的同时,尽量降低写放大系数。因为过多的Compaction会占用大量磁盘IO资源,进而影响系统整体效率。这就像在有限的道路上,车辆过多就会造成交通堵塞,降低整体通行效率。

三、Doris中Compaction相关参数及原理

Doris中用于控制Compaction的参数众多,理解这些参数的含义并合理调整,对适配不同场景至关重要。下面从几个关键方面进行介绍。

(一)数据版本的产生

用户的数据表会依据分区和分桶规则,被切分成若干数据分片(Tablet)存储在不同的BE节点上,每个Tablet通常有3个副本(默认值)。Compaction在每个BE节点上独立执行,处理该节点上的所有数据分片。

Doris的数据写入以微批方式进行,每一批次的数据针对每个Tablet都会形成一个rowset,而一个Tablet由多个Rowset组成。每个Rowset都有对应的起始版本和终止版本,新增Rowset的起始和终止版本相同,如[6 – 6]、[7 – 7]等。经过Compaction,多个Rowset会合并成一个大的Rowset,其起始和终止版本为多个版本的并集,例如[6 – 6]、[7 – 7]、[8 – 8]合并后变为[6 – 8] 。

Rowset的数量对Compaction能否及时完成影响很大。例如,假设一个集群有3个BE节点,每个BE节点2块盘,有一张表,2个分区,每个分区3个分桶,默认3副本。那么总分片数量为(2 * 3 * 3) = 18个,若均匀分布在所有节点上,每个盘上有3个tablet。若一次导入涉及其中一个分区,则一次导入总共产生9个Rowset,平均每块盘产生1 – 2个Rowset(实际情况中,Tablet可能集中在某一块磁盘上)。由此可见,rowset的数量直接取决于表的分片数量。所以,合理设置表的分区、分桶和副本数量,避免过多分片,能够有效降低Compaction的开销。

这里给出一些建议配置:一个表的Tablet总数量等于 (Partition num * Bucket num) 。需要注意的是,一个Partition的Bucket数量一旦确定,不可更改。因此在确定Bucket数量时,要预先考虑集群扩容的情况。例如,当前只有3台host,每台host有1块盘,若Bucket数量只设置为3或更小,后期即使增加机器,也无法提高并发度。在不同数据量的情况下,可参考以下分桶数设置:

  • 数据大小为0MB – 10MB时,Bucket数量为1;
  • 10MB – 50MB时,Bucket数量为2;
  • 50MB – 2GB时,Bucket数量为4;
  • 2GB – 5GB时,Bucket数量为8;
  • 5GB – 25GB时,Bucket数量为16;
  • 25GB – 50GB时,Bucket数量为32;
  • 大于50GB时,Bucket数量为64。

同时要注意,分桶数过多会导致FE元数据信息负载过高,影响导入和查询性能;分桶数太少则无法满足数据量增长的需求。

(二)Base和Cumulative两种类型的Compaction意义和解决思路

在Doris中,存在Base Compaction(简称BC)和Cumulative Compaction(简称CC)两种类型。

Doris在选择数据分片进行Compaction时,默认策略是每次选择数据版本最多的数据分片进行操作。但这种策略存在一定问题,因为版本多的分片不一定是最频繁访问的分片,而Compaction的主要目的是优化读性能。这可能导致“写多读少”的表一直在进行Compaction,而“读多写少”的表却不能及时得到处理,进而影响读性能。

为解决这一问题,Doris在选择数据分片时引入了“读取频率”因素。“读取频率”和“版本数量”会根据各自的权重,综合计算出一个Compaction分数。分数越高的分片,越优先进行Compaction。这两个因素的权重由以下BE参数控制(取值越大,权重越高):tablet_score = k1 * scan_frequency + k2 * compaction_score ,其中k1和k2分别可以通过参数compaction_tablet_scan_frequency_factor(默认值为0)和参数compaction_tablet_compaction_score_factor(默认值为1)动态配置。scan_frequency表示tablet当前一段时间的scan频率(可在监控面板上查看),compaction_score分数可以表征compaction任务从任务池每次可以拿到的permit令牌大小,每个tablet的permit越大,说明排队执行的任务越多。

(三)调整参数优化Compaction逻辑(以routine load阶段为例)

  1. BE参数调整方法
    • 增加Compaction线程数:max_base_compaction_threads默认是4,可以适当增加,加快Base Compaction的速度;max_cumu_compaction_threads默认是每个盘1个,也可适当增加,加快Cumulative Compaction的速度。
    • 调整Compaction任务数:compaction_task_num_per_disk默认是4,适当增加可允许每个磁盘上执行更多的Compaction任务;compaction_task_num_per_fast_disk默认是8,同样可适当增加。
    • 调整Compaction任务配额:total_permits_for_compaction_score默认是10000,适当增加可允许更多的Compaction任务同时进行。
    • 调整Compaction任务的内存使用:max_cumulative_compaction_num_singleton_deltas默认是1000,可以适当减少,以限制每个Cumulative Compaction任务合并的版本数,从而减少内存使用。
  2. 优化策略
    • 优化数据导入频率和批量大小:增大单次数据导入的量,降低导入频率,以此减少compaction的压力。
    • 合理设置表的分区和分桶:通过合理的分区和分桶策略,避免生成过多的数据分片,降低compaction的复杂性。
    • 避免频繁的删除操作:尽量减少使用DELETE操作,因为DELETE操作会在底层生成Delete版本,增加compaction的负担。
    • 监控和报警:通过监控Compaction的压力和相关指标,及时发现和处理问题,防止compaction压力过大。

具体操作步骤
调整BE参数:修改be.conf文件,增加Compaction线程数和任务数,保存文件并重启BE节点,使配置生效。
优化数据导入:增大单次数据导入的量,降低导入频率。例如,将sink.batch.size设置为10000,sink.batch.interval设置为10秒。
合理设置表的分区和分桶:根据数据量和查询需求,合理设置表的分区和分桶。对于大数据量的表,可以增加分区数和分桶数。
监控和报警:使用Grafana和Prometheus监控Compaction的压力和相关指标,设置报警阈值,及时发现和处理问题。通过这些调整和优化策略,可以有效降低routine load阶段的compaction压力,提高系统的稳定性和查询性能。

四、tablet不健康排查方案

在存算分离模式下进行小文件合并观测验证时,若遇到问题,首先要排查compaction score高的原因。找出score最高的若干个tablet,这些通常是用户高频导入的表。

然后分析score最高的tablet,查看是否是compaction持续失败导致compaction score高。可以通过以下命令定位频繁进行compaction行为的tablet:

grep "permits" log/be.INFO | tail -n 1000 

为调节BE节点compaction的内存使用量,Doris增加了对compaction任务提交的permission机制,系统通过参数total_permits_for_compaction_score配置维持一定数量的compaction permits。

此外,还可以通过以下命令查看tablet正在进行的compaction操作:

curl http://xxxxxxxx:8040/api/compaction/show?tablet_id=192775 

执行上述命令后,会得到类似如下的json数据:

{ "cumulative policy type": "SIZE_BASED", "cumulative point": 18438, "last cumulative failure time": "1970-01-01 08:00:00.000", "last base failure time": "1970-01-01 08:00:00.000", "last cumulative success time": "2021-05-05 17:18:48.904", "last base success time": "2021-05-05 16:14:49.786", "rowsets": [ "[0-17444] 1 DATA NONOVERLAPPING 0200000000b1fb8d344f83103113563dd81740036795499d 2.86 GB", "[17445-17751] 1 DATA NONOVERLAPPING 0200000000b25183344f83103113563dd81740036795499d 68.61 MB", "[17752-18089] 1 DATA NONOVERLAPPING 0200000000b2b9a2344f83103113563dd81740036795499d 74.52 MB", "[18090-18437] 1 DATA NONOVERLAPPING 0200000000b32686344f83103113563dd81740036795499d 76.41 MB", "[18438-18678] 1 DATA NONOVERLAPPING 0200000000b37084344f83103113563dd81740036795499d 53.07 MB", "[18679-18679] 1 DATA NONOVERLAPPING 0200000000b36d87344f83103113563dd81740036795499d 3.11 KB", "[18680-18680] 1 DATA NONOVERLAPPING 0200000000b36d70344f83103113563dd81740036795499d 258.40 KB", "[18681-18681] 1 DATA NONOVERLAPPING 0200000000b36da0344f83103113563dd81740036795499d 266.98 KB" ], "stale_rowsets": [], "stale version path": [] } 

从这些数据中,我们可以了解到一个tablet的Cumulative Point,最近一次成功、失败的BC/CC任务时间,以及每个rowset的版本信息。例如,从上述示例可以得出:基线数据量大约在2 – 3GB,增量rowset增长到几十MB后就会晋升到BC任务区;新增rowset数据量很小,且版本增长较快,表明这是一个高频小批量的导入场景。

通过对Doris compaction运维方法的全面了解,从原理到实际问题的解决,再到参数调整和排查方案,希望能帮助大家更好地管理和优化Doris系统,提升其在数据处理过程中的性能表现。