以StarRocks 新发布的3.2.1版本为基准,剖析了Compaction任务管理器设计架构,分析了基于Size-Tiered挑选rowset进行Compaction的策略,介绍了Compaction的调度执行流程。最后,针对两种常见问题场景,给出Compaction性能调优思考。
一、引言
为了充分发挥磁盘连续写性能优势,StarRocks以 Append-only 的方式将数据写入内存,达到阈值后Flush为磁盘文件,再结合 Compaction 机制将多个小文件进行多路归并排序形成新的文件,最终实现数据的高效读。数据表会按照分区和分桶规则,切分成若干个数据分片(Tablet),存储在不同BE节点上。当用户写入一批数据时,BE节点在相关Tablet中会形成一个 rowset。一个Tablet是由多个rowset组成的。每个rowset都有一个对应的起始版本和终止版本。对于新增rowset,起始版本和终止版本相同,表示为[2-2]。多个rowset经过Compaction形成一个大的rowset,起始版本和终止版本为多个版本的并集,如[2-2]、[3-3]、[4-4]合并后变成[2-4]。
Compaction分为两种类型:Base Compaction和Cumulative Compaction。
Base compaction会将rowset合入到start version为0的基线数据版本中,同时将标记为删除的行的删掉,以减少数据占用的空间,是一种开销较大的Compaction操作。而Cumulative Compaction 先对增量数据进行合并,当增量数据合并后的大小达到一定阈值后,再和基线数据合并。
二、Compaction任务管理器架构
在StarRocks新发布的3.2.1版本中,Compaction架构已经发生了很大变化。
首先,Compaction机制由即时创建工作线程转变为生产(Check)-消费(Schedule)模型。这种机制在Compaction频率不高的场景下,剔除了那些闲置的工作线程,节约了资源。其次,挑选rowset进行Compaction的策略也升级了。因此,本文以新架构为基准进行介绍。
图 1 Compaction任务管理器架构
Check线程周期性从该BE管理的Tablet中挑选出一批(batch_size)需要Compaction的Tablet,将其包装成CompactionCandidate,并提交到有序集合_compaction_candidates中。Schedule线程定时获取队列中Score最高的任务进行Compaction条件判断。每次有新的Compaction任务满足条件,并且当前运行的Compaction任务数量没有达到最大Compaction任务数,就启动一个线程去处理Compaction。如图1所示为Compaction任务管理器架构。
三、基于Size-Tiered挑选rowset的策略
上述任务管理器是如何判断Tablet需要Compaction?挑选Tablet中哪些rowset进行Compaction?StarRocks使用了两个策略。一个是default策略,另一个是Size-Tiered策略。Default策略实质是统计rowset中segment文件数量,挑选出1000个segment文件进行合并。这个策略会导致Cumulative Compaction任务执行时间长,占用资源多,因此在新版本中已经不再使用。Size-Tiered策略是将rowset大小分层,结合影响因素计算出Rowset的Compaction的Score。Score分数越高,rowset越容易被执行Compaction。
表1中的三个配置作用于这个Size-Tiered策略。在该策略下,rowset根据大小分为7层,相邻两层之间大小差距5倍,每一层都会保留1个rowset。默认配置下7个层级大小依次128K、640K、3M、15M、78M、390M、1.9G。
size_tiered_min_level_size | 128K |
size_tiered_level_multiple | 5 |
size_tiered_level_num | 7 |
(表1 Size_Tiered策略相关配置项)
影响Score的因素有五个,分别为Tablet模型、rowsets大小、segment 数量、Tablet Version数量和rowsets level。在Tablet模型因素中,明细模型因为没有计算逻辑,因此在其他因素相同情况下,rowset越大,Compaction优先级越高。聚合模型和更新模型在segment数量多情况下,Compaction后文件大小会减少,读效率会提高。因此,这两个模型在计算Score时增加了segment数量影响权重。在考虑rowset level因素对Score的影响时,level越低表示压缩的数据量越小,Compaction执行优先级越高。在Version数据达到Tablet最大Version时,数据就写不进去,BE进而报错。因此在处理Version 数量时,一旦达到最大版本数的90%时,就说明需要Compaction,在计算Score时也是最为激进。
图2 Score影响因素
Score分数最高的的rowsets,也并不是一定能提交到有序集合中,需要满足segment数量原则,才能成为预备Compaction任务入队。此举也是为了避免频繁触发Compaction影响集群性能而被引入。新引入的Size-Tiered策略相比较于default策略,将Tablet中rowset分为7层,每次挑选出同一层内的rowset进行Compaction。Cumulative Compaction会执行多次,每次逐步向上一层传递。这种策略减少了Base Compaction的触发次数,提高Compaction效率。
四、Compaction调度执行流程
Schedule线程周期性获取_compaction_candidates集合中Score最高的candidate并对它进行检查。检查的逻辑包括Tablet状态校验、Compaction锁状态、每个磁盘上运行的任务数限制和任务失败间隔约束等四个。
在检查不通过情况下,重新获取candidate,直到检查通过为止。接下来是决策Compaction执行方式。如果segment数量较少且待合并的列数于每组最大数,那么就使用行合并。最后就是初始化Compaction任务并提交到工作线程执行。根据挑选出的rowsets按版本升序排序后第一个版本号是否是0,决定此次任务的类型。如果是0,任务就是Base Compaction,否则是Cumulative Compaction。虽然任务分为两种类型,但是执行流程却相同。
执行流程主要分为Shortcut Compaction、Data Compaction、Valid Compaction和Commit Compaction等四个步骤,下面一一介绍:
-
Shortcut Compaction。这个过程是一种快捷Compaction的方式,即无需磁盘IO即可完成Compaction。需要以下前提条件,第一个条件就是此次任务只有一个rowset参与,第二个条件是rowset中segment文件数量至少两个,且文件之间没有数据重叠,第三个条件是rowset没有被Compaction过,第四个条件是执行方式是行式。它的原理是使用文件系统的硬链接能力。
-
Data Compaction。将挑选出rowsets进行合并,生成一个output rowset。在执行rowsets合并时,会创建一个Reader和一个rowset writer,rowset writer与output rowset相对应。在Reader底层逻辑中,input rowsets中的每一个rowset都会对应一个rowset reader。Reader按照key的排序规则逐行读出input rowsets中的数据,然后通过Rowset Writer写入output rowset。Cumulative Compaction不会将删除的数据行进行真正地删除,这部分工作会在Base Compaction中进行。
-
Valid Compaction。这部分所做的工作就比较简单了,校验下Compaction输入行数和输出行数、合并行数、过滤行数总和是否相同。
-
Commit Compaction。这部分工作就是修改下Tablet关于rowset的元数据部分,包括删除旧的版本和添加新版本、close掉输入的rowset和加载rowset。
图3 Compaction任务调度执行流程
五、性能调优思考
Compaction原理机制如上述三个部分介绍。实际生产环境下,需要面对各种各样问题调优。下面以两个场景,做个调优思考,希望抛砖引玉。
第一个场景是Tablet中版本数量有上升趋势或者数值较高,则可以从以下几个方面思考:
第一个方面,是否是Schedule线程中Check Candidate不通过?如果是,可以修改两个参数base_compaction_num_threads_per_disk和cumulative_compaction_num_threads_per_disk,根据任务类型,增加这两个参数,让同时能够执行更多的 Compaction 任务;第二方面,是否是Check线程中Tablet中的rowset不满足被Compaction的条件?可以从Size-Tiered挑选rowset策略入手。
方法主要有两个:
一是降低最小segment数据门槛,可以修改的参数有size_tiered_level_multiple和min_cumulative_compaction_num_singleton_deltas;另一个是缩小执行Base Compaction的间隔。
第二个场景是Compaction占用内存过多,从两个方面考虑:
一是是否是Compaction任务过多导致的内存占用过多?如果是这样,把Compaction任务数调小即可;另一方面是,Compaction的执行方式,尽可能用列式Compaction,通过配置较小的vertical_compaction_max_columns_per_group,让Compaction串行列式方式执行。
上面是两种典型的Compaction调优场景。实际生产环境问题怪象百出、难以定位,因此需要具体问题具体分析。
六、总结
本文以StarRocks新发布的3.2.1版本为基准,介绍了新架构下生产者-消费者模型的Compaction任务管理框架。针对任务管理框架的核心问题,即如何挑选rowset进行Compaction,介绍了基于大小分层的rowset挑选策略,并分析了Compaction紧迫程度评估指标Score的影响因素,总结了该策略的优点;其次阐述了Compaction的调度执行流程;最后,从两个典型场景角度出发,作了Compaction性能调优的思考。
StarRocks通过Compaction机制将不同的数据版本进行聚合,将小文件合并成大文件,非常有效地提升了查询性能。
七、作者介绍
陈海峰,中国移动云能力中心数据库产品部-OLAP数据库开发工程师。主要参与OLAP内核优化/数据存储/Zero-ETL相关技术的研发。