Cube优化原理
首先要先了解Cuboid生成树。如图1所示,在Cube中,所有的Cuboid组成一个树形结构,根节点是全维度的Base Cuboid,再依次逐层聚合掉每个维度生成子Cuboid,直到出现0个维度时结束。图1中绿色部分就是一条完整的Cuboid生成路径。预计算的过程实际就是按照这个流程构建所有的Cuboid。
图1 Cuboid生成树
通过这颗Cuboid生成树,我们不难发现:当维度数量过多,就会导致Cuboid数量以指数级膨胀;如果维度基数过大,还会使所在的Cuboid结果集变大。这些都是影响Cube膨胀率和构建时间的重要因素。
但是,所有的Cuboid都是必要的吗?实际上,在多数情况下,我们并不需要这里的每一个Cuboid,因此需要对Cuboid生成树做剪枝。剪枝可以从两个方面入手:数据特性、查询需求。首先介绍数据特性,考虑下图2的两个Cuboid,左侧Cuboid包含4个维度(ABCD),右侧Cuboid包含3个维度(ABC),而两个Cuboid都包含相同(或极度相近)行数的记录,说明读取两个Cuboid结果的代价是一样的,同时左侧Cuboid除了具有右侧Cuboid的查询支持能力外,还能支持带有维度D的查询,因此右侧Cuboid就可以被去除。
图2 去除冗余Cuboid
再考虑查询需求,在报表或多维分析场景中,有些维度是每次查询都会出现的,如年份;有些维度总是一起出现的,如开始时间、结束时间;有些维度间是有层级关系的,如商品分类或地理信息。充分利用查询的这些实际需求也能去除不需要的Cuboid,例如:如果年份是必要的,那么所有不包含年份维度的Cuboid都可以被去除;如果两个维度总是同时出现,那么这这些维度单独出现的Cuboid就可以被去除。
在Apache Kylin中,可以通过设置Cube的维度组合规则来去除无用的Cuboid。首先,可以通过定义聚合组对维度分组,只在每个聚合组内生成Cuboid。此外,在单个聚合组内部,还可以设置维度组合规则,如:必须维度用于定义一定出现的维度、联合维度用于定义一组同时出现的维度、层级维度用于定义一组有层级关系的维度,详细的Cuboid生成规则如下图3所示: