赞
踩
① 创建中间表
② 将中间表的数据均匀分配到不同的文件
通常将1百万条数据分到同一文件,因为1百万条数据通常小于HDFS
的Block Szie
128M。
③ 创建维度字典表
④ 构建cube
⑤ 构建Hbase K-V
Cuboid id
+维度值组成了HBase
的RowKey
,这样大大减少了数据量,Cuboid id
每个位即各维度是否存在,存在为1,不存在为0。
⑥ 将cube data
转成HFile
格式并导入HBase
我们知道,一个N
维的Cube
,是由1个N
维子立方体、N
个N-1
维子立方体、N*(N-1)/2
个N-2
维子立方体、…、N
个1维子立方体和1个0维子立方体构成,总共有2^N
个子立方体组成,在逐层算法中,按维度数逐层减少来计算,每个层级的计算(除了第一层,它是从原始数据聚合而来),是基于它上一层级的结果来计算的。比如,[Group by A, B]
的结果,可以基于[Group by A, B, C]
的结果,通过去掉C
后聚合得来的;这样可以减少重复计算;当 0维度Cuboid
计算出来的时候,整个Cube
的计算也就完成了。
每一轮的计算都是一个MapReduce
任务,且串行执行;一个N
维的Cube
,至少需要N
次MapReduce Job
。
算法优点:
MapReduce
的优点,处理了中间复杂的排序和shuffle
工作,故而算法代码清晰简单,易于维护;Hadoop
的日趋成熟,此算法非常稳定,即便是集群资源紧张时,也能保证最终能够完成。算法缺点:
Cube
有比较多维度的时候,所需要的MapReduce
任务也相应增加;由于Hadoop
的任务调度需要耗费额外资源,特别是集群较庞大的时候,反复递交任务造成的额外开销会相当可观;Mapper
逻辑中并未进行聚合操作,所以每轮MR
的shuffle
工作量都很大,导致效率低下。HDFS
的读写操作较多:由于每一层计算的输出会用做下一层计算的输入,这些Key-Value
需要写到HDFS
上;当所有计算都完成后,Kylin
还需要额外的一轮任务将这些文件转成HBase
的HFile
格式,以导入到HBase
中去;总体而言,该算法的效率较低,尤其是当Cube维度数较大的时候。
也被称作逐段(By Segment
) 或逐块(By Split
) 算法,从1.5.x
开始引入该算法,该算法的主要思想是,每个Mapper
将其所分配到的数据块,计算成一个完整的小Cube
段(包含所有Cuboid
)。每个Mapper
将计算完的Cube
段输出给Reducer
做合并,生成大Cube
,也就是最终结果。如图所示解释了此流程。
与旧算法相比,快速算法主要有两点不同:
Mapper
会利用内存做预聚合,算出所有组合;Mapper
输出的每个Key
都是不同的,这样会减少输出到Hadoop MapReduce
的数据量,Combiner
也不再需要;MapReduce
便会完成所有层次的计算,减少Hadoop
任务的调配。从之前章节的介绍可以知道,在没有采取任何优化措施的情况下,Kylin
会对每一种维度的组合进行预计算,每种维度的组合的预计算结果被称为Cuboid
。假设有4个维度,我们最终会有2^4 =16个Cuboid
需要计算。
但在现实情况中,用户的维度数量一般远远大于4个。假设用户有10 个维度,那么没有经过任何优化的Cube
就会存在2^10 =1024个Cuboid
;而如果用户有20个维度,那么Cube
中总共会存在2^20 =1048576个Cuboid
。虽然每个Cuboid
的大小存在很大的差异,但是单单想到Cuboid
的数量就足以让人想象到这样的Cube
对构建引擎、存储引擎来说压力有多么巨大。因此,在构建维度数量较多的Cube
时,尤其要注意Cube
的剪枝优化(即减少Cuboid
的生成)。
衍生维度用于在有效维度内将维度表上的非主键维度排除掉,并使用维度表的主键(其实是事实表上相应的外键)来替代它们。Kylin
会在底层记录维度表主键与维度表其他维度之间的映射关系,以便在查询时能够动态地将维度表的主键“翻译”成这些非主键维度,并进行实时聚合。
虽然衍生维度具有非常大的吸引力,但这也并不是说所有维度表上的维度都得变成衍生维度,如果从维度表主键到某个维度表维度所需要的聚合工作量非常大,则不建议使用衍生维度。
聚合组(Aggregation Group
)是一种强大的剪枝工具。聚合组假设一个Cube
的所有维度均可以根据业务需求划分成若干组(当然也可以是一个组),由于同一个组内的维度更可能同时被同一个查询用到,因此会表现出更加紧密的内在关联。每个分组的维度集合均是Cube
所有维度的一个子集,不同的分组各自拥有一套维度集合,它们可能与其他分组有相同的维度,也可能没有相同的维度。每个分组各自独立地根据自身的规则贡献出一批需要被物化的Cuboid
,所有分组贡献的Cuboid
的并集就成为了当前Cube
中所有需要物化的Cuboid
的集合。不同的分组有可能会贡献出相同的Cuboid
,构建引擎会察觉到这点,并且保证每一个Cuboid
无论在多少个分组中出现,它都只会被物化一次。
对于每个分组内部的维度,用户可以使用如下三种可选的方式定义,它们之间的关系,具体如下:
① 强制维度(Mandatory
)
如果一个维度被定义为强制维度,那么这个分组产生的所有CuboId
中每一个CuboId
都会包含该维度。每个分组中都可以有0个、1个或多个强制维度。如果根据这个分组的业务逻辑,则相关的查询一定会在过滤条件或分组条件中,因此可以在该分组中把该维度设置为强制维度。
② 层级维度(Hierarchy
)
每个层级包含两个或更多个维度。假设一个层级中包含D1
,D2
…Dn
这n
个维度,那么在该分组产生的任何CuboId
中, 这n
个维度只会以()
,(D1)
,(D1,D2)
…(D1,D2…Dn)
这n+1
种形式中的一种出现。每个分组中可以有0个、1个或多个层级,不同的层级之间不应当有共享的维度。如果根据这个分组的业务逻辑,则多个维度直接存在层级关系,因此可以在该分组中把这些维度设置为层级维度。
③ 联合维度(Joint
)
每个联合中包含两个或更多个维度,如果某些列形成一个联合,那么在该分组产生的任何CuboId
中,这些联合维度要么一起出现,要么都不出现。每个分组中可以有0个或多个联合,但是不同的联合之间不应当有共享的维度(否则它们可以合并成一个联合)。如果根据这个分组的业务逻辑,多个维度在查询中总是同时出现,则可以在该分组中把这些维度设置为联合维度。
这些操作可以在Cube Designer
的Advanced Setting
中的Aggregation Groups
区域完成,如下图所示:
聚合组的设计非常灵活,甚至可以用来描述一些极端的设计。假设我们的业务需求非常单一,只需要某些特定的CuboId
,那么可以创建多个聚合组,每个聚合组代表一个CuboId
。具体的方法是在聚合组中先包含某个CuboId
所需的所有维度,然后把这些维度都设置为强制维度。这样当前的聚合组就只能产生我们想要的那一个CuboId
了。
再比如,有的时候我们的Cube
中有一些基数非常大的维度,如果不做特殊处理,它就会和其他的维度进行各种组合,从而产生一大堆包含它的CuboId
。包含高基数维度的CuboId
在行数和体积上往往非常庞大,这会导致整个Cube
的膨胀率变大。如果根据业务需求知道这个高基数的维度只会与若干个维度(而不是所有维度)同时被查询到,那么就可以通过聚合组对这个高基数维度做一定的“隔离”。我们把这个高基数的维度放入一个单独的聚合组,再把所有可能会与这个高基数维度一起被查询到的其他维度也放进来。这样,这个高基数的维度就被“隔离”在一个聚合组中了,所有不会与它一起被查询到的维度都没有和它一起出现在任何一个分组中,因此也就不会有多余的CuboId
产生。这点也大大减少了包含该高基数维度的CuboId
的数量,可以有效地控制Cube
的膨胀率。
Kylin
会把所有的维度按照顺序组合成一个完整的RowKey
,并且按照这个RowKey
升序排列CuboId
中所有的行。设计良好的RowKey
将更有效地完成数据的查询过滤和定位,减少IO
次数,提高查询速度,维度在RowKey
中的次序,对查询性能有显著的影响。
RowKey
的设计原则如下:
① 被用作where
过滤的维度放在前边
② 基数大的维度放在基数小的维度前边
当Segment
中某一个Cuboid
的大小超出一定的阈值时,系统会将该Cuboid
的数据分片到多个分区中,以实现Cuboid
数据读取的并行化,从而优化Cube
的查询速度。具体的实现方式如下:构建引擎根据Segment
估计的大小,以及参数kylin.hbase.region.cut
的设置决定Segment
在存储引擎中总共需要几个分区来存储,如果存储引擎是HBase
,那么分区的数量就对应于HBase
中的Region
数量。kylin.hbase.region.cut
的默认值是5.0,单位是GB
,也就是说对于一个大小估计是50GB
的Segment
,构建引擎会给它分配10个分区。用户还可以通过设置kylin.hbase.region.count.min
(默认为1)和kylin.hbase.region.count.max
(默认为500)两个配置来决定每个Segment
最少或最多被划分成多少个分区。
由于每个Cube
的并发粒度控制不尽相同,因此建议在Cube Designer
的Configuration Overwrites
(上图所示)中为每个Cube
量身定制控制并发粒度的参数。假设将把当前Cube
的kylin.hbase.region.count.min
设置为2,kylin.hbase.region.count.max
设置为100。这样无论Segment
的大小如何变化,它的分区数量最小都不会低于2,最大都不会超过100。相应地,这个Segment
背后的存储引擎(HBase
)为了存储这个Segment
,也不会使用小于两个或超过100个的分区。我们还调整了默认的kylin.hbase.region.cut
,这样50GB
的Segment
基本上会被分配到50个分区,相比默认设置,我们的Cuboid
可能最多会获得5倍的并发量。
可以与Kylin
结合使用的可视化工具很多,例如:
Tableau
、Excel
、PowerBI
等工具集成;Saiku
、BIRT
等Java
工具集成;JavaScript
、Web
网页集成;Kylin
开发团队还贡献了Zepplin
的插件,也可以使用Zepplin
来访问Kylin
服务。
引入依赖:
<dependency>
<groupId>org.apache.kylin</groupId>
<artifactId>kylin-jdbc</artifactId>
<version>2.5.1</version>
</dependency>
代码:
public class KylinTest { //驱动 public static final String KYLIN_DRIVER = "org.apache.kylin.jdbc.Driver"; //URL public static final String KYLIN_URL = "jdbc:kylin://hadoop100:7070/FirstProject"; //用户名 public static final String KYLIN_USER = "admin"; //密码 public static final String KYLIN_PASSWORD = "KYLIN"; public static void main(String[] args) throws ClassNotFoundException, SQLException { //加载驱动 Class.forName(KYLIN_DRIVER); //获取连接 Connection con = DriverManager.getConnection(KYLIN_URL, KYLIN_USER, KYLIN_PASSWORD); //预编译sql PreparedStatement ps = con.prepareStatement("SELECT SUM(SAL) FROM EMP GROUP BY DEPTNO"); //执行查询 ResultSet result = ps.executeQuery(); while (result.next()) { System.out.println(result.getInt(1)); } } }
结果:
① Zepplin
安装与启动
解压:
[root@hadoop100 sorfware]# tar -zxvf zeppelin-0.8.0-bin-all.tgz -C /opt/module/
启动
[root@hadoop100 zeppelin-0.8.0]# bin/zeppelin-daemon.sh start
访问http://hadoop102:8080
查看web
页面
② 配置Zepplin
支持Kylin
点击右上角anonymous
选择Interpreter
:
搜索Kylin
插件并修改相应的配置并保存:
③ 案例实操
点击Notebook
创建新的note
:
执行查询:
其他图表格式:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。