近几年,Apache Kylin做为一个高速的开源分布式大数据查询引擎正在迅速崛起。它充分发挥Hadoop、Spark、HBase等技术的优点,经过对超大规模数据集进行预计算,实现秒级甚至亚秒级的查询响应时间,同时提供标准SQL接口。目前,Apache Kylin已在全球范围获得了普遍应用,如百度、美团、今日头条、eBay等,支撑着单个业务上万亿规模的数据查询业务。在超高性能的背后,Cube是相当重要的核心。一个优化得当的Cube既能知足高速查询的须要,又能节省集群资源。本文将从多个方面入手,介绍如何经过优化Cube提高系统性能。服务器
在传统多维分析就有多维立方体(OLAP Cube)的概念。Apache Kylin在大数据领域对Cube进行了扩展,经过执行 MapReduce/Spark任务构建Cube,对业务所需的维度组合和度量进行预聚合,当查询到达时直接访问预计算聚合结果,省去对大数据的扫描和运算,这就是Apache Kylin高性能查询的基本实现原理。并发
如图1所示,Apache Kylin会对SQL的查询计划进行改写,把源表扫描、多表链接、指标聚合等在线计算转换成对预计算结果的读取,极大减小了在线计算和I/O读写的代价。 而查询所访问的预计算结果保存在Cuboid当中(见图1红色方框),Cuboid大小只和维度列的基数有关,和源数据行数无关,这使得查询的时间复杂度能够取得一个量级的提高。分布式
图 - 1预计算查询计划oop
一个Cuboid对应着一组分析的维度,并保存了度量的聚合结果。Cube就是全部Cuboid的集合,如图2所示,每一个节点表明一个Cuboid。当查询到达,Apache Kylin会根据SQL所使用的维度列在Cube中选择最合适的Cuboid,最大程度地节省查询时间。性能
图 - 2 Cube示意图大数据
社区不乏一些使用Apache Kylin的成功案例分享,但常常还会看到不少朋友遇到性能问题,例如SQL查询过慢、Cube构建时间过长甚至失败、Cube膨胀率太高等等。究其缘由,大多数问题都是因为Cube设计不当形成的。所以,合理地进行Cube优化就显得尤其重要。优化
这里先分享两个社区用户进行优化的案例:ui
案例1 – 提高Cube查询效率spa
背景:某智能硬件企业使用Apache Kylin做为大数据平台查询引擎,对查询性能有较高要求,但愿提升查询效率。.net
数据:
优化方案:
优化成果:
案例二 – 提高Cube构建效率
背景:某金融企业使用Apache Kylin做为报表分析引擎,发现Cube膨胀率多大、构建时间过长,但愿对这一状况进行改善。
硬件:20台高配置PC服务器
数据:事实表有100多万条记录,度量是某些列的平均值
优化方案:
优化成果:
从以上案例能够看出,经过Cube调优能够显著改善Apache Kylin的构建性能、查询性能及Cube膨胀率。那么这些改进的背后到底是什么原理呢?
为了深刻理解Cube,首先要先了解Cuboid生成树。如图3所示,在Cube中,全部的Cuboid组成一个树形结构,根节点是全维度的Base Cuboid,再依次逐层聚合掉每一个维度生成子Cuboid,直到出现0个维度时结束。图3中绿色部分就是一条完整的Cuboid生成路径。预计算的过程实际就是按照这个流程构建全部的Cuboid。
图 - 3 Cuboid生成树
经过这颗Cuboid生成树,咱们不难发现:当维度数量过多,就会致使Cuboid数量以指数级膨胀;若是维度基数过大,还会使所在的Cuboid结果集变大。这些都是影响Cube膨胀率和构建时间的重要因素。
可是,全部的Cuboid都是必要的吗?实际上,在多数状况下,咱们并不须要这里的每个Cuboid,所以须要对Cuboid生成树作剪枝。剪枝能够从两个方面入手:数据特性、查询需求。首先介绍数据特性,考虑下图的两个Cuboid,左侧Cuboid包含4个维度(ABCD),右侧Cuboid包含3个维度(ABC),而两个Cuboid都包含相同(或极度相近)行数的记录,说明读取两个Cuboid结果的代价是同样的,同时左侧Cuboid除了具备右侧Cuboid的查询支持能力外,还能支持带有维度D的查询,所以右侧Cuboid就能够被去除。
图 - 4 去除冗余Cuboid
再考虑查询需求,在报表或多维分析场景中,有些维度是每次查询都会出现的,如年份;有些维度老是一块儿出现的,如开始时间、结束时间;有些维度间是有层级关系的,如商品分类或地理信息。充分利用查询的这些实际需求也能去除不须要的Cuboid,例如:若是年份是必要的,那么全部不包含年份维度的Cuboid均可以被去除;若是两个维度老是同时出现,那么这这些维度单独出现的Cuboid就能够被去除。
在Apache Kylin中,能够经过设置Cube的维度组合规则来去除无用的Cuboid。首先,能够经过定义(1)聚合组对维度分组,只在每一个聚合组内生成Cuboid。此外,在单个聚合组内部,还能够设置维度组合规则,如:(2)必须维度用于定义必定出现的维度、(3)联合维度用于定义一组同时出现的维度、(4)层级维度用于定义一组有层级关系的维度,详细的Cuboid生成规则以下图所示:
图 - 5聚合组规则
维表中能够由主键推导出值的列能够做为衍⽣维度。使用场景:以星型模型接入时。例如用户维表能够从userid推导出用户的姓名,年龄,性别。优化效果:维度表的N个维度组合成的cuboid个数会从2的N次方降为2。
汇集组:用来控制哪些cuboid须要计算。
适用场景:不是只须要计算base cuboid的状况下,都须要汇集组。
注意事项:一个维度能够出如今多个汇集组中,可是build期间只会计算一次。
若是不设置汇集组,默认状况下只会计算 base cuboid。
汇集组不宜太多。
强制维度:全部cuboid必须包含的维度,不会计算不包含强制维度的cuboid。
适用场景:能够将肯定在查询时必定会使用的维度设为强制维度。例如,时间维度。
优化效果:将一个维度设为强制维度,则cuboid个数直接减半。
联合维度:将几个维度视为一个维度。
适用场景: 1 能够将肯定在查询时必定会同时使用的几个维度设为一个联合维度。
2 能够将基数很小的几个维度设为一个联合维度。
3 能够将查询时不多使用的几个维度设为一个联合维度。
优化效果:将N个维度设置为联合维度,则这N个维度组合成的cuboid个数会从2的N次方减小到1。
层次维度:具备必定层次关系的维度。
使用场景:像年,月,日;国家,省份,城市这类具备层次关系的维度。
优化效果:将N个维度设置为层次维度,则这N个维度组合成的cuboid个数会从2的N次方减小到N+1。
在OLAP分析场景中,常常存在对某个id进行过滤,但查询结果要展现为name的状况,好比user_id和user_name。这类问题一般有三种解决方式:
a. 将ID和Name都设置为维度,查询语句相似
select name, count(*) from table where id = 1 group by id,name
。这种方式的问题是会致使维度增多,致使预计算结果膨胀;b. 将id和name都设置为维度,而且将二者设置为联合。这种方式的好处是保持维度组合数不会增长,但限制了维度的其它优化,好比ID不能再被设置为强制维度或者层次维度;
c. 将ID设置为维度,Name设置为特殊的Measure,类型为Extended Column。这种方式既能保证过滤id且查询name的需求,同时也不影响id维度的进一步优化。
因此此类需求咱们推荐使用 Extended Column。
简单的讲,查询频率越高的维度在Rowkey中的顺序须要越靠前。
当Segment中某个Cuboid的大小超出必定的阈值时,系统会将该Cuboid的数据分片到多个Hbase Region Server,从而实现Cuboid数据读取的并行化,优化Cube的查询速度。
kylin的默认设置中
kylin.storage.hbase.min-region-count=1,
kylin.storage.hbase.max-region-count=500,
kylin.storage.hbase.region-cut-gb=5
在实际应用中(根据实际数据量调整),能够将
kylin.storage.hbase.min-region-count=2,
kylin.storage.hbase.max-region-count=100,
kylin.storage.hbase.region-cut-gb=1
上面设置为最小为2个分区,每一个分区大小为1G,最多设置100个region分区。
[1] Apache Kylin 深刻Cube和查询优化
http://geek.csdn.net/news/detail/199615
[2] Apache Kylin 维度优化指南
[3] 【技术帖】Apache Kylin Cube优化方式