Kylin Cube优化

一、剪枝优化( 根据查询样式)

若不进行任何剪枝,那么cuboid数为2^n个,n为维度的个数
Cube膨胀率:cube大小/原始表的大小
Cube合理膨胀率:0~1000%

膨胀率过高可能的原因:

  1. 维度太多:没有进行很好的剪枝,导致cuboid数过多
  2. 存在高基数维度:一个维度的基数过高,会导致所有包含该维度的cuboid数据量过大,这些cuboid累计在一起导致整体Cube过大
  3. 存在占用空间较大的度量:比如count distinct,导致cuboid的每一行都保存一个较大的寄存器(hllc/bitmap),极端情况下可能每行都有几十k,导致cube整体很大

1. Aggregation Group 聚合组

(1)Mandatory Columns 强制维度

如果某个维度在查询中一定会在过滤条件或者分组条件中,那么可以将该维度设置为强制维度。

如果某个维度被定义为强制维度,那么这个Cube构建产生的所有Cuboid中每一个Cuboid都会包含该维度。

(2)Hierarchy Dimensions 层级维度

如果分组业务逻辑中,多个维度之间存在层级关系,则可以将这些维度设置为层级维度。

假设一个层级中包含D1,D2,…,Dn这n个维度,那么在构建产生的所有Cuboid中,这n个维度只会以(),(D1),(D1,D2)…(D1,D2,…,Dn)这n+1种形式中的一种出现。

(3)Joint Dimensions 联合维度

如果多个维度在查询中总是同时出现,则可以把这些维度设置为联合维度。

如果某些维度形成一个联合,那么构建产生的所有Cuboid中,这些联合维度要么一起出现,要么都不出现。

2. Derived Columns 衍生维度

在创建Cube时,选择维度表中某维度为衍生维度,那么该维度将不参与Cube构建时有效维度的组合,将由维度表的主键(事实表的外键)来替代它。Kylin底层会记录维度表主键与维度表其他维度的映射关系,以便在查询时能动态的将维度表的主键“翻译”成非主键维度。

如果维度表主键到某个维度表维度所需要的聚合工作量非常大,那么将该维度作为一个普通维度,而不是一个衍生维度可能是一个更好的选择。

二、Rowkey优化(根据查询样式)

通过改变维度顺序来改变Rowkey,从而优化查询HBase的速度。

简单的讲,查询频率越高的维度在Rowkey中的顺序需要越靠前。

维度顺序设计原则:

  1. 在查询中经常被用过过滤条件的维度放在其他维度前面
  2. 将经常出现在查询中的维度放在不经常出现的维度前面
  3. 基数较高的维度,如果查询可能被用作过滤条件,那么将它往前调整

三、并发粒度优化(存储优化)

构建引擎会根据Segment估计的大小,以及参数“kylin.hbase.region.cut”的设置决定Segment在存储引擎中总共需要几个分区来存储,如果存储引擎是HBase,那么分区数目就对应于HBase中的Region数量。kylin.hbase.region.cut的默认值是5.0,单位是GB,也就是说对于一个大小估计是50GB Segment,构建引擎会给它分配10个分区。用户还可以通过设置kylin.hbase.region.count.min(默认为1)和kylin.hbase.region.count.max(默认为500)两个配置来决定每个Segment最少或最多被划分成多少个分区。

由于每个Cube的并发粒度控制不尽相同,因此建议在Cube Designer的Configuration Overwrites中为每个Cube量身定制控制并发粒度的参数。

四、维度编码(存储优化)

编码(Encoding)代表了该维度的值应使用何种方式进行编码,合适的编码能够减少维度对空间的占用,例如,我们可以把所有的日期都用三个字节进行编码,相比于字符串存储,或者是使用长整数形式存储的方法,我们的编码方式能够大大减少每行Cube数据的体积。而Cube中可能存在数以亿计的行数,使用编码节约的空间累加起来将是一个非常巨大的数字。

目前Kylin支持的编码方式有以下几种:

  • Date:3个字节编码,支持0000-01-01到9999-01-01;
  • Time:4个字节编码,支持1970-01-01 00:00:00到2038-01-19 03:14:07;
  • Dict:维度所有可能的值创建一个字典文件,然后使用字典中每个值的编号来编码,该种方式比较紧凑,比较节省空间,但只适合维度基数小而长度较长的维度;
  • Fixed_length:需要指定长度,使用固定长度的字节来存储代表维度值的字节数组,该数组为字符串形式的维度值的UTF-8字节,适合基数大、长度也大的维度;
  • Int:需要指定长度,编码int32,指定长度为4,编码int64,指定长度为8,如果维度取值很小,可以指定长度为1或2,这样可以节省很多空间。

五、去重指标与全局字典优化

Kylin支持精确去重(Bitmap)和非精确去重(Hyperloglog)。

对于非精确的去重,Kylin提供了多种可选精度,现挑选其中的几种进行对比,见表6-1。精度越高,占用的空间也越大。

表6-1 Count Distinct精度和占用空间列表

img

注意:
Hyperloglog算法,在基数比较小的情况下,精确度会很低,这个时候推荐使用精确去重。

Kylin精确去重指标的优化:

参考:Apache Kylin 精确去重和全局字典权威指南

  • Case1 : 全局字典的复用
  • Case2: 1个Cube只有1个超高基数列的精确去重指标
  • Case3: 1个Cube有多个超高基数列的精确去重指标
  • Case4: 精确去重指标无需跨Segment上卷聚合
  • Case5: Cube是非分区的
  • Case6: 一个Cube包含较多精确去重指标

六、Segment的合并与清理

随着增量构建出来的Segment的慢慢累积,Cube的查询性能将会变差,因为每次跨Segment的查询都需要从存储引擎中读取每一个Segment的数据,并且在查询引擎中对不同Segment的数据做进一步的聚合,这对于查询引擎和存储引擎来说都是巨大的压力。

1.合并Segment

在Web GUI中选中需要进行Segments合并的Cube,单击Action→Merge,然后在对话框中选中需要合并的Segment,可以同时合并多个Segment,但是这些Segment必须是连续的。单击提交后系统会提交一个类型为“MERGE”的构建任务,它以选中的Segment中的数据作为输入,将这些Segment的数据合并封装成为一个新的Segment(如图3-5所示)。这个新的Segment的起始时间为选中的最早的Segment的起始时间,它的结束时间为选中的最晚的Segment的结束时间。

用户也可以使用Rest接口触发合并Segments:

1
2
3
4
5
6
7
8
9
PUT /kylin/api/cubes/{cubeName}/build

Path Variable
- cubeName - required string Cube name.

Request Body
- startTime - required long Start timestamp of data to build
- endTime - required long End timestamp of data to build
- buildType - required string Supported build type: ‘BUILD’, ‘MERGE’, ‘REFRESH’

我们需要将buildType设置为MERGE,并且将startTime设置为选中的需要合并的最早的Segment的起始时间,将endTime设置为选中的需要合并的最晚的Segment的结束时间。

2.自动合并

在Cube Designer的“Refresh Settings”的页面中有“Auto Merge Thresholds”和“Retention Threshold”两个设置项可以用来帮助管理Segment碎片。

“Auto Merge Thresholds”允许用户设置几个层级的时间阈值,层级越靠后,时间阈值就越大。

举例来说,用户可以为一个Cube指定(7天、28天)这样的层级。每当Cube中有新的Segment状态变为READY的时候,就会触发一次系统试图自动合并的尝试。系统首先会尝试最大一级的时间阈值,结合上面的(7天、28天)层级的例子,首先查看是否能将连续的若干个Segment合并成为一个超过28天的大Segment,在挑选连续Segment的过程中,如果遇到已经有个别Segment的时间长度本身已经超过了28天,那么系统会跳过该Segment,从它之后的所有Segment中挑选连续的累积超过28天的Segment。如果满足条件的连续Segment还不能够累积超过28天,那么系统会使用下一个层级的时间阈值重复寻找的过程。每当找到了能够满足条件的连续Segment,系统就会触发一次自动合并Segment的构建任务,在构建任务完成之后,新的Segment被设置为READY状态,自动合并的整套尝试又需要重新再来一遍。

3.保留Segment

及时清理不再使用的Segment。在很多业务场景中,只会对过去一段时间内的数据进行查询,例如对于某个只显示过去1年数据的报表,支撑它的Cube事实上只需要保留过去一年内的Segment即可。由于数据在Hive中往往已经存在备份,因此无需再在Kylin中备份超过一年的历史数据。

在这种情况下,我们可以将“Retention Threshold”设置为365。每当有新的Segment状态变为READY的时候,系统会检查每一个Segment:如果它的结束时间距离最晚的一个Segment的结束时间已经大于“RetentionThreshold”,那么这个Segment将被视为无需保留。系统会自动地从Cube中删除这个Segment。

注意:
如果启用了“Auto Merge Thresholds”,那么在使用“RetentionThreshold”的时候需要注意,不能将“Auto Merge Thresholds”的最大层级设置得太高。否则这两个参数之前会发生冲突,Segment越来越大不会得到释放。

七、其它优化

1.增量构建与全量构建

全量构建可以看作增量构建的一种特例:在全量构建中,Cube中只存在唯一的一个Segment,该Segment没有分割时间的概念,因此也就没有起始时间和结束时间。全量构建和增量构建各有其适用的场景,用户可以根据自己的业务场景灵活地进行切换。全量构建和增量构建的详细对比如表3-1所示。

表3-1 全量构建和增量构建的对比

img

2.灵活运用视图

对于维度表比较大的情况,或者select查询部分存在复杂的逻辑判断,或者存在Kylin不支持的函数、语句或类型时,可以现在Hive中对事实表和维度表进行关联的逻辑处理,并创建Hive视图,之后根据试图创建Cube模型。

TODO:
官网还提供了很多优化方法,可以参考:http://kylin.apache.org/cn/docs/howto/howto_optimize_build.html


参考: