Kylin实战(三):cube聚合组优化

Kylin核心思想是通过预计算空间换时间,是典型的Multidimensional OLAP应用。MLOAP有着结构紧凑,查询响应快的优点,但同时也有着臭名昭著的维度爆炸问题。如果将所有维度组合都预计算并存储下来,组合的数量将是2^N。通常一个宽表有上百的维度是很正常的,但查询组合往往不超过20维,其中巨大的浪费是显而易见的。

聚合组(Aggregation Groups)

为了处理维度爆炸的问题,从1.5版本开始(目前最高版本为2.1)Kylin引入了聚合组(Aggregation Groups)特性来对2^N的全量维度组合进行剪枝。聚合组是基于不同查询模式通常是相互独立的假设上设计的,比如对于销售数据,市场总监更关心各产品在各地区的销售量分布,而HR或许只关心每个销售团队和每个人的销售额。

假设一份数据有20个维度,可以分为两个独立的查询模式(聚合组),查询的维度数分别为10维和15维,则原本 2^20个 的维度组合(在Kylin中称为cuboid)可以大大减少至 2^10 + 2^15 约等于 2^15 个cuboid。中间如果不同查询组合产生了相同的cuboid,Kylin会意识到并跳过。

完全未剪枝的维度组合树

使用聚合组剪枝后的维度组合树

在剪枝中被去掉的维度组合没有被物化,并不意味着不能查询了。当所有cuboid都不能满足查询时,Kylin会选出离查询组合最为接近并包含该查询组合的cuboid来计算出结果。比如查询组合是[A,B]而该组合没有被物化,同时3维的cuboid有[A,B,C]、[A,B,D],此时Kylin会选出两个组合里id较小(通常也意味着基数较小)的一个来进行上卷(roll up)得到[A,B]。在最坏的情况下,Kylin需要从base cuboid,也就是包含所有原始数据的基础cuboid计算出最终结果。

聚合组内剪枝

使用聚合组显著地减轻了维度爆炸问题,然而在聚合组内部还有剪枝优化的空间。Kylin提供三个特定的剪枝方式供用户在相应的场景下对组内的维度组合进行再一次剪枝。

  1. 强制维度(Mandatory Dimensions)
    强制维度指维度组合必须包含的维度,比如对日活跃用户的查询必须包含日期dt,则可以将dt设为强制维度,不包含dt的cuboid将不会被物化。每指定一个强制维度相当与将维度数减一,维度组合数变为原来的一半。

  2. 层级维度(Hierarchy Dimensions)
    层级维度指只会以层级形式出现的维度,比如国家-省份-城市是典型的维度组合,我们一般会查询国家,国家-省份和国家-省份-城市,而很少单独查询城市。被指定为层级维度的几个维度只会以前缀匹配的形式出现,对总体维度组合数目的增加倍数由2^N减少为N。

  3. 合并维度(Joint Dimensions)
    合并维度指在查询中或都出现或都不出现的维度组合,比如邮箱用户名和邮箱服务提供商,也就是@前后两部分分成两列(某些情况下真有可能这么做)。由于合并维度的出现是一致的,那么计算时只需要考虑出现或不出现两种情况,所有对总体维度组合数目的增加倍数由2^N减少为2。

通过聚合组剪枝和组内剪枝,剩余的cuboid基本符合预设的查询模式,也就是必需的。

总结

聚合组是Kylin处理维度爆炸问题的最重要也是最有效的方法,同时不在聚合组内的查询模式可以通过HBase原始数据的上卷满足,在查询速度和可用性间做了很好的平衡,大大方便了cube的建模。从Kylin查询用户的角度来说,在最早的版本,遇到未命中cuboid的查询时Kylin会同步调用Hive进行计算或直接报错,用户体验比较差,所以通常倾向于浪费一定的计算和存储资源来满足用户查询的conner case,用户查询也要尽可能小心不要超出cube的范围。而现在的版本,cuboid对用户来说是不可见的,少见的查询条件返回时间长一点是可以接受的。

参考文献

1.Kylin官方博客 new-aggregation-group
2.Apache Kylin权威指南
3.Apache Kylin 高级设置:聚合组(Aggregation Group)原理解析
4.Wikipedia OLAP

本文是原创文章,转载请注明:时间与精神的小屋 - Kylin实战(三):cube聚合组优化