新型聚合组设计,以解决维度诅咒问题(尤其是存在高基数性维度)

摘要

维度诅咒,是所有预计算OLAP引擎的严重问题;
在1.5之前, kylin使用一些简单的技术处理这个问题,也减轻了问题的严重程度;
在开源实践过程中,我们发现这些技术缺乏系统性设计思维,也无法处理很多常见问题;
在1.5,我们重新设计了聚合组(aggregation group)机制,以更好地适应所有类型的cube设计场景;

介绍

已知的是,Kylin通过预计算"cube集"加快查询速度,意味着包含所有维度的不同组合,也就是"cuboid集";
产生的问题就是,随着维度(dimension)的增长,cuboid集会指数级增长;
举个例子,3个维度时cube会有8个cuboid,4个维度时就有16个cuboid(2^N);
即使Kylin使用可扩展的计算框架(MapReduce)和存储(HBase)来计算和存储cube集,如果cube大小几倍于原始数据也是不能接受的;

解决方案是裁剪不需要的维度.在优化cube设计里讨论过的,有两种方式:
第一个方式是:移除不需要的维度.

例如,假设有一张日期查找表,cal_dt字段为主键,衍生出其他字段如week_begin_dt, month_begin_dt.即使查询时需要week_begin_dt字段(即:维度),也可以从字段cal_dt计算出来,所以可以裁剪week_begin_dt字段,这称为衍生优化(“derived” optimization);

如Kylin示例的kylin_cal_dt表:

cal_dt year_beg_dt month_beg_dt week_beg_dt
2012-08-16 2012-01-01 2012-08-01 2012-08-11
2012-01-03 2012-01-01 2012-01-01 2012-01-01

第二个方式是:维度集中的一些组合可以裁剪.这是本章讨论的重点,称为"维度组合裁剪".
例如,一个维度定义为"必要的",所有没有包含这个维度的维度组合,都可以裁剪.
如果维度A,B,C构成"层级关系",维度组合A,AB或ABC会保留下来.
1.5之前,Kylin也有"聚合组"的概念,也用于维度组合裁剪.但是有很少的文档,很难理解.所以会跳过1.5,重新定义"聚合组".

原先维度组合裁剪技术的缺点:1,这些技术是各自独立的,没有系统性;2,没有良好设计和文档化,很难外部使用;3,没有足够多的描述语义(describing semantics);

为了说明描述语义的问题,假设一个数据cube,有一个高基数维度buyer_id,以及一些正常的维度如日期cal_dt,buyer所在的城市等.

一个分析需要按非buyer_id维度分组以获得全局印象,如以cal_dt分组.分析也需要下钻,检查指定的buyer的行为,按buyer_id过滤;而buyer_id具有高基数性,一旦buyer_id确定了,相关的记录就会很少.在这个例子中,预期的裁剪策略的输出为:

Cuboid 计算 或 跳过 理由
city 计算 支持 以城市分组 的查询
cal_dt 计算 支持 以日期分组 的查询
buyer_id 跳过 以buyer分组,会产生大量的结果,buyer_id应该用于在基本cuboid里查询时做为过滤条件
city,cal_dt 计算 支持 以城市和日期分组 的查询
city,buyer_id 跳过 以buyer分组,会产生大量的结果,buyer_id应该用于在基本cuboid里查询时做为过滤条件
cal_dt,buyer_id 跳过 以buyer分组,会产生大量的结果,buyer_id应该用于在基本cuboid里查询时做为过滤条件
city,cal_dt,buyer_id 计算 基本cuboid

在Kylin 1.5之前,没有设置裁剪策略的语义工具;

新型聚合组设计

在Kylin 1.5,我们重新设计了聚合组机制.参见Kylin Cuboid Whitelist,新设计允许cube设计者定义预期的cuboid集白名单;

新设计中,"聚合组"定义为cuboid集的集群,并提供分片规则.Cube设计者可以为一个cube定义多个聚合组,所有包含有效"维度组合"的聚合组集合并为cube提供cuboid集;注意,一个cuboid可以出现在多个聚合组里,并且在构建cube时只会计算一次;

在聚合组的源码里,有两个重要的属性定义:@JsonProperty("includes")@JsonProperty("select_rule")

@JsonProperty("includes"): 定义聚合组里包含哪些维度;值必须是全部维度的子集;只包含必要的维度,以使列表最小;

@JsonProperty("includes"):聚合组集中有效的cuboid集的选取规则.cube设计者可以定义多条规则应用于所包含的维度,当前有三类的规则:

  • 层级的规则(Hierarchy rules)
  • 必要的规则(Mandatory rule)
  • 可连接的规则(Joint rules): 两个或更多的维度是"可连接的(joint)",则有效的cuboid要么没有这些维度,要么全部有这些维度.即这些维度总是在一起的.当可以确定一些维度总是会一起查询时,这个规则就很有用;也用于很少使用的维度的维度组合裁剪.假设你有20个维度,前10个维度经常用于查询,后10个很少用于查询.通过设置后10个维度为"可连接的",可以将cuboid数量从220降到211;这也是旧的聚合组机制所做的.如果使用1.5之前的,元数据更新工具会自动将这个翻译为可连接的语义;为灵活的应用聚合组,理论上可以控制cuboid是计算还是跳过;这可以显著的降低计算量和存储量,尤其当cube是为固定的面板展示使用时,这种应用是重用sql查询,只请求一些特定的cuboid集;极端的用例,可以配置每个聚合组只包含一个cuboid,多个聚合组包含了所需要的cuboid白名单;

Kylin的cuboid计算周期器(scheduler),会基于定义的聚合组来安排有效cuboid的计算顺序.你不用关心具体实现,因为每个cuboid只会计算一次.唯一要关心的是:不要滥用聚合组.多应用聚合组的选取规则,避免引使用大理的单cuboid聚合组,除非有必要.过多的聚合组是cuboid计算周期器的负担,对查询引擎也一样;

再看buyer_id问题

有了新的聚合组这个工具,buyer_id问题可以这样解决,cube可以定义两个聚合组:

  • 聚合组1包含: [cal_dt, city, buyer_id] 选取规则:{joint:[cal_dt,city,buyer_id]}
  • 聚合组2包含: [cal_dt,city] 选取规则:{}

聚合组1只用于基本cuboid;聚合组2将应用所有没有buyer_id的cuboid集;

你可能感兴趣的:(#,Kylin,kylin,聚合组,aggregation,group)