Kylin构建Cube优化
【Kylin 技术贴倾情奉献——夏天小厨原创】Apache Kylin官宣它的定位是一款千亿级OLAP引擎,在其高性能的背后,Cube的构建显得至关重要。因此优化Cube也成为数据分析必不可少的技能之一,那为什么要优化Cube呢?因为随着维度数的增加,Cuboid的数量会随其爆炸式递增,如果不优化Cube,不仅Build Cube时间会很漫长,而且Cube的膨胀率也会让你害怕,从而占用大量的磁盘空间。优化之后不仅能够节省集群资源,而且能够满足高速查询的需要。
为了减轻Cube构建的压力,减少Cuboid的数量以及Build N-Dimension Cuboid : level的层数,kylin开放了一系列的高级设置(即大家常见的Advanced Setting),帮助用户构建成真正需要的Cuboid,减少不必要的资源时间和空间上的浪费。但是再往清楚的说,kylin cube的优化实际上就是对维度的优化,将你未来查询时需要的维度放进去,对他们进行组合、编排,从而达到剪枝优化的目的。举个简单的例子,一个业务场景有20个维度,如果不进行优化剪枝,那么这个Cube中总共会存在2^20=1 048 576个Cuboid,但是优化之后就可大大减小,详细剪枝优化后的效果请听下面分解。
一、检查Cube状况
那么怎么判断你的Cube构建的是否合理呢?主要看两个指标:1、Cuboid的数量;2、Cube的大小(膨胀率);
1、检查Cuboid的数量
个人觉得这个地方需要掌握的理论知识没多少,我们只需要知道这此创建的Cube下属的Cuboid是否是细粒度的,即每种情况的cuboid都会计算到。如果有,那就应该去修改Cube了。使用命令
kylin.sh org.apache.kylin.engine.mr.common.CubeStatsReader + CubeName即可查询。
[aaa@qq.com ~]# kylin.sh org.apache.kylin.engine.mr.common.CubeStatsReader iot_gas_cube
Statistics of IoT_Gas_Cube[0_52831]
Cube statistics hll precision: 14
Total cuboids: 15
Total estimated rows: 784602
Total estimated size(MB): 5.378857135772705
Sampling percentage: 100
Mapper overlap ratio: 1.0
Mapper number: 1
Length of dimension CSERVER.IOT_GAS_TABLE.REAL_TIME is 2
Length of dimension CSERVER.IOT_GAS_TABLE.PS is 2
Length of dimension CSERVER.IOT_GAS_TABLE.PQ is 2
Length of dimension CSERVER.IOT_GAS_TABLE.DEVICEID is 1
Length of dimension CSERVER.IOT_GAS_TABLE.LOCATION is 1
Length of dimension CSERVER.IOT_GAS_TABLE.ENERGYTYPE is 1
|---- Cuboid 111111, est row: 53128, est MB: 0.41
|---- Cuboid 101111, est row: 53523, est MB: 0.38, shrink: 100.74%
|---- Cuboid 100111, est row: 52862, est MB: 0.35, shrink: 98.77%
|---- Cuboid 100011, est row: 46234, est MB: 0.3, shrink: 87.46%
|---- Cuboid 101011, est row: 53047, est MB: 0.37, shrink: 99.11%
|---- Cuboid 110111, est row: 52616, est MB: 0.38, shrink: 99.04%
|---- Cuboid 110011, est row: 52448, est MB: 0.36, shrink: 99.68%
|---- Cuboid 111011, est row: 53015, est MB: 0.39, shrink: 99.79%
|---- Cuboid 111100, est row: 52691, est MB: 0.38, shrink: 99.18%
|---- Cuboid 101100, est row: 53006, est MB: 0.35, shrink: 100.6%
|---- Cuboid 100100, est row: 53298, est MB: 0.33, shrink: 100.55%
|---- Cuboid 101000, est row: 51465, est MB: 0.33, shrink: 97.09%
|---- Cuboid 110100, est row: 52540, est MB: 0.35, shrink: 99.71%
|---- Cuboid 110000, est row: 51720, est MB: 0.33, shrink: 98.44%
|---- Cuboid 111000, est row: 53009, est MB: 0.37, shrink: 100.6%
从分析结果的下半部分可以看到,所有的Cuboid及它的分析结果都以树状的形式打印了出来。在这棵树中,每个节点代表一个Cuboid,每个Cuboid都由一连串1或0的数字组成,如果数字为0,则代表这个Cuboid中不存在相应的维度;如果数字为1,则代表这个Cuboid中存在相应的维度。除了最顶端的Cuboid之外,每个Cuboid都有一个父亲Cuboid,且都比父亲Cuboid少了一个“1”。其意义是这个Cuboid就是由它的父亲节点减少一个维度聚合而来的(上卷)。最顶端的Cuboid称为Base Cuboid,它直接由源数据计算而来。
2、检查Cube的大小
另外一种比较直观的方法就是通过在kylin 的web界面去查看Cube是否构建的较好,将光标放到cube size栏就能出现相关的信息,如下图所示:
大家可以看出来我的这个Cube的膨胀率只有324.18%。一般来说,Cube的膨胀率应该在0%~1000%之间,如果一个Cube的膨胀率超过1000%,那么Cube管理员应当开始挖掘其中的原因。通常,膨胀率高有以下几个方面的原因。
(1) Cube中的维度数量较多,且没有进行很好的Cuboid剪枝优化,导致Cuboid数量极多;
(2) Cube中存在较高基数的维度,导致包含这类维度的每一个Cuboid占用的空间都很大,这些Cuboid累积造成整体Cube体积变大;
(3) 存在比较占用空间的度量,例如Count Distinct,因此需要在Cuboid的每一行中都为其保存一个较大的寄存器,最坏的情况将会导致Cuboid中每一行都有数十KB,从而造成整个Cube的体积变大;
因此,对于Cube膨胀率居高不下的情况,管理员需要结合实际数据进行分析,可灵活地运用接下来介绍的优化方法对Cube进行优化。(这段话来自Apache Kylin 权威指南)
二、Cube构建优化方法
1、聚合组(Aggravation Group)
【聚合组概念】:是根据业务的现有维度组合,划分出具有强依赖性的维度组,说白了就是按照需求将维度分组,这些组合称为聚合组。在聚合组内,各维度之间的组合会预计算,但聚合组之间并不交叉预计算,从而减少Cuboid的数量。
【优化效果】:举个例子,业务场景有4个维度,分别为ABCD,如果聚合组含有的维度为ABCD的话,它的Cuboid为2^4=16个。但是此时如果AB是一个聚合组,BC是一个聚合组,那么Cuboid的个数就是2^2+2^2=8个,相当于缩减了一半。即原来2^(K+M+N)个Cuboid可以减少到2^K+2^M+2^N个。
注意:一个维度可以出现在多个聚集组中,但是build期间只会计算一次,且聚合组不宜过多。
2、强制维度(Mandatory Dimensions)
其实将剩下几个模块列成和聚合组是一级的标题,有点欠缺,因为聚合组里面含有的优化方式,就是接下来我们要将的三种。但是为了大家能够直观的看各种优化方式,我们暂且就这么写,但是在概念上一定要区分清楚。
【强制维度】:如果将一个维度设置成强制维度,那么所有的Cuboid都会含有这个维度,换句话说kylin 只会预计算含有这个维度的Cuboid。大家可能还是不太理解,强制维度到底能干嘛,这么说吧,如果你之后每次查询 group by时都要带上某个维度,那么这个维度就可以设置为强制维度,换句话说,比如你将A维度设置成强制维度, 你支持group by没写上A,则会出错。
【优化效果】:那么Mandatory Dimensions到底有什么好处呢?它能将Cuboid的数量减少一半。如下图所示
【使用场景】:Group by时肯定会存在的维度,例如时间/区域等。
3、层次维度(Hierarchy Dimensions)
【层次维度】:具有上下级层次关系的维度,例如时间维度 年—>月—>日,和地区维度 国家(country)—>省份(province)—> 城市(city)。
【优化效果】:如果有三个维度A,B,C 设置为层次维度,那么Cuboid数量将由2^3减为3+1(ABC、AB、A、空)。如下图所示
【使用场景】:Group by时只会出现以下三种情况:
- group by country, province, city(等同于 group by country, city 或者group by city)
- group by country, province(等同于group by province)
- group by country。
如果跳过上一级,直接访问下一级例如 group by province,city,则会直接出错。
4、联合维度(Joint Dimensions)
【联合维度】:如果将几个维度被当做联合维度,那么构建的Cuboid,要么都包含这几个维度,要不都不含有。换句话说就是把这几个维度当做一个维度来看。
【优化效果】:将多个维度细化成一个维度,如下图所示
【使用场景】:假如有ABC三个维度,但是在查询的时候只会出现Group by A,B,C,而不会出现Group A,Group by B,Group by A、B等等这种情况,维度联合查询。
三、Rowkey优化方法
Apache Kylin使用HBase做为Cube的存储引擎。HBase是Hadoop上的Key-Value数据库,支持按Key的随机查询与写入,这个Key在HBase中称为Rowkey;为了能够支持按多个维度进行查询,Kylin需要将多个维度值以某种次序组成Rowkey。排在Rowkey靠前部分的维度,将比排在靠后部分的维度更易于做筛选(可以直接使得HBase Scan Range大幅缩小),因此查询效率更高。除了各维度在Rowkey上的次序外,维度的编码方法对于空间占用及查询性能也有着显著的影响。
在Cube调优页面的Rowkey模块,可以查看到Rowkey的基础信息和查询统计。基础信息包含基数、字段类型、编码以及是否为shardby 。查询统计包含维度出现的频次、作为过滤条件出现的次数占比、作为聚合条件出现的占比。并且,基数、出现频次、过滤百分比及聚合百分比都支持排序,用户可从多个角度对Rowkey进行优化
-
按照基数降序排列,基数较大的Rowkey可以放置基数小的前面,例如时间维度一般放在前面;
-
按照过滤出现次数占比排序,比例较高的Rowkey放置过滤次数较少的维度前面。
-
按照访问频率,访问频率高的Rowkey放置频率较低维度前面。
Rowkey编码方式详解
- Dict编码:使用字典将长的值映射成短的ID,适合中低基数的维度,默认推荐编码。但由于字典要被加载到Kylin内存中,在超高基情况下,可能引起内存不足的问题。
- Fixed_Length编码:适用于超高基场景,将选取字段的前N个字节作为编码值,当N小于字段长度,会造成字段截断,当N较大时,造成RowKey过长,查询性能下降。只适用于varchar或nvarchar类型。
- Fixed_Length_Hex编码:适用于字段值为十六进制字符,比如1A2BFF或者FF00FF,每两个字符需要一个字节。只适用于varchar或nvarchar类型.
- Integer编码:将数值类型字段直接用数字表示,不做编码转换。Integer编码需要提供一个额外的参数“Length”来代表需要多少个字节。Length的长度为1到8,支持的整数区间为[ -2^(8*N-1), 2^(8*N-1)]
- Date编码:将日期类型的数据使用三个字节进行编码,支持的格式包括yyyyMMdd、yyyy-MM-dd、yyyy-MM-dd HH:mm:ss、yyyy-MM-dd HH:mm:ss.SSS,其中如果包含时间戳部分会被截断。
- Time编码:对时间戳字段进行编码,支持范围为[ 1970-01-01 00:00:00, 2038/01/19 03:14:07],毫秒部分会被忽略。time编码适用于time, datetime, timestamp等类型。
- Boolean编码:用一个byte表示布尔值,适用于字段值为: true, false,1, 0等