增量构建Cube及优化

Cube的概念不进行介绍了,有不了解的可以参考上一篇文章。Cube的全量构建不进行过多的描述,在Kylin的web界面中可以进行全量的构建。
在大多数业务场景中,数仓中的数据处于不断增长的状态,为了支持在构建Cube时,无需重复处理历史数据,则引入了增量构建功能。
全量构建和增量构建区别如下:

全量构建 增量构建
每次更新时都需要更新整个数据集 每次只对需要更新的时间范围进行更新 ,计算量相对较小
查询时不需要合并不同Segment的结果 查询时需要合并不同Segment的结果,因此查询性能受影响
不需要后续的Segment合并 累计一定量的Segment之后,需要进行合并
适合小数据量或全表更新的Cube 适合大数据量的Cube

全量构建与增量构建Cube查询方式对比:

  1. 全量构建Cube
    • 查询引擎只需要向存储引擎访问单个Segment所对应的数据,不需要进行Segment之间的聚合
    • 为了加强性能,单个Segment的数据也有可能被分片存储到引擎的多个分区上,查询引擎可能仍然需要对单个Segment不同分区的数据做进一步的聚合
  2. 增量构建Cube
    • 不同时间的数据分布在不同的Segment之中,查询引擎需要向存储引擎请求读取各个Segment的数据
    • 增量构建的Cube上的查询会比全量构建做更多的运行时聚合,增量构建的查询会比全量构建Cube查询要更慢一些

Kylin将增量构建的Cube划分为了多个Segment,所对应的就是HBase中的一个表。Segment其含义如下:

  1. 一个Cube可能由1个或者多个Segment组成。Segment是指定时间范围的Cube,可以理解为Cube的分区
  2. Segment是针对数据源中的某一个片段计算出来的Cube数据,代表一段时间内数据源的计算结果
  3. 每个Segment用起始时间和结束时间来标志
  4. 一个Segment的起始时间等于它之前Segment的结束时间,它的结束时间等于它后面那个Segment的起始时间
  5. 同一个Cube下不同的Segment除了背后的数据源不同之外,其他的结构定义、构建过程、优化方法、存储方式都完全相同

Segment的管理

增量构建的Cube每天都可能会有新的增量,最终Cube可能包含很多Segment,这样将导致Kylin性能受到严重影响。

  • 从执行引擎的角度来说,运行时的查询引擎需要聚合多个Segment的结果才能返回正确的查询结果
  • 从存储引擎的角度来说,大量的Segment会带来大量的文件,给存储空间带来巨大的压力

所以说我们需要控制Cube中的Segment的数量,为了保持查询性能,可以做以下几点:

  • 定期地将某些Segment合并在一起
  • 根据Segment保留策略自动地淘汰那些不会被查询到的陈旧Segment
  • 在Kylin的Web UI中选中需要进行Segment合并的Cube
  • 使用Web UI删除Cube的Segment
  • 由于手动维护Segment很繁琐,人工成本也高,Kylin的Web UI也可以支持自动合并Segment

Cube的优化

Cuboid特指Kylin中在某一种维度组合下所有计算的所有数据。以减少Cuboid数量为目的的优化统称为Cuboid剪枝。在没有任何优化措施的情况下,Kylin会对每一种维度的组合进行预计算。
假如有4个维度,可能最终会有2^4=16个Cuboid需要计算;10个维度的话,没有经过任何优化的Cube就会存在在 2^10=1024个Cuboid。以此类推,更多的维度会有更多Cuboid。
过多的Cuboid数量对构建引擎、存储引擎压力非常巨大。因此,在构建维度数量较多的Cube时,需要注意Cube的剪枝优化。剪枝优化是一种试图减少额外空间占用的方法,这种方法的前提是不会影响查询时间。
Kylin提供了一些简单的工具来帮助我们完成Cube的剪枝优化,可以使用如下命令
kylin.sh org.apache.kylin.engine.mr.common.CubeStatsReader cube_name(cube_name为新建的cube名称
也可以在Web UI的Model页面选择一个READY状态的Cube,光标移到该Cube的Cube Size列时,Web UI会提示Cube的源数据大小,以及当前Cube的大小除以数据源大小的比例,也称为膨胀率(Expansion Rate))
正常情况下Cube的膨胀率应该在0%~1000%之间,如果一个Cube的膨胀率超过1000%,我们应该从以下几个方面查找原因:

  • Cube中的维度数量较多,且没有进行很好的Cuboid剪枝优化,导致Cuboid数量过多
  • Cube中存在较高基数的维度(基数的维度是指维度中有多个不同的值),导致包含这类维度的每一个Cuboid占用的空间都很大,这些Cuboid累计造成整体Cube体积变大
  • 存在占用空间大的度量,例如Count Distinct,因此需要在Cuboid的每一行中都为其保存一个较大度量数据,最坏的情况将会导致Cuboid中每一行都有数十KB,从而造成整个Cube的体积变大

Cube衍生维度

一个维度可以是普通维度或者衍生维度(Derived),将维度表的维度设置为衍生维度,这个为欸都不会参与计算,而是使用维度表的主键或者事实表的外键来替代它。
Kylin会在底层记录维表主键与维度表其他为维度之间的映射关系,以便在查询时能够动态地将维度表的主键翻译成这些非主键维度,并进行实时聚合。
创建Cube的时候,这些维度如何指定为衍生维度,Kylin将会排除这些维度,而是使用维度表的主键来代替它们创建Cuboid。后续查询的时候在基于主键聚合结果,再进行一次聚合。
虽然使用衍生维度时可以有效减少Cube中Cuboid的数量,但在查询时会增加聚合的时间。如果维度表主键到某个维度表维度所需要的聚合工作量非常大,此时作为一个普通的维度聚合更合适,否则会影响Kylin的查询性能。

聚合组

随着维度数目的增加,Cuboid的数量会不断的增长。为了缓解Cube的构建压力,Apache Kylin引入了一系列的高级设置,可以帮助我们筛选出真正的Cuboid。这些高级设置包括:

  • 聚合组(Aggregation Group):默认Kylin会把所有维度放在同一聚合组中
  • 强制维度(Mandatory Dimension):指的是那些总会出现在where条件或group by语句中的维度
  • 层级维度(Hierarchy Dimension):是指一组有层级关系的维度(如:国家、省、市)
  • 联合维度(Joint Dimension):将多个维度视作一个维度,在进行组合计算的时候,它们要么一起出现,要么均不出现。通常适用于以下几种情形
    • 总是在一起查询的维度
    • 彼此之间有一定映射关系
    • 技术很低的维度,如性别、布尔类型的属性