第2章
快 速 入 门
第1章介绍了Apache Kylin的概况,以及它与其他SQL-on-Hadoop技术的不同,相信读者对Apache Kylin有了一个整体的认识。本章将详细介绍Apache Kylin的一些核心概念,然后带领读者一步步创建Cube,构建Cube,并通过SQL来查询Cube,使读者对Apache Kylin有更为直观的了解。
2.1 核心概念
在使用Apache Kylin之前,需要先了解一下Apache Kylin中的各种概念和术语,为后续章节的学习奠定基础。
2.1.1 数据仓库、OLAP与BI
数据仓库(Data Warehouse)是一种信息系统的资料储存理论,此理论强调的是利用某些特殊资料储存方式,让所包含的资料特别有利于分析处理,从而产生有价值的资讯并依此做决策。
利用数据仓库方式存放的资料,具有一旦存入,便不随时间变化而变动的特性,此外,存入的资料必定包含时间属性,通常,一个数据仓库会含有大量的历史性资料,并且它利用特定分析方式,从中发掘出特定的资讯。
OLAP(Online Analytical Process),即联机分析处理,它可以以多维度的方式分析数据,并且能弹性地提供上卷(Roll-up)、下钻(Drill-down)和透视分析(Pivot)等操作,是呈现集成性决策信息的方法,其主要功能在于方便大规模数据分析及统计计算,多用于决策支持系统、商务智能或数据仓库。与之相区别的是联机交易处理(OLTP),联机交易处理侧重于基本的、日常的事务处理,包括数据的增、删、改、查。
- OLAP需要以大量历史数据为基础,配合时间点的差异并对多维度及汇整型的信息进行复杂的分析。
- OLAP需要用户有主观的信息需求定义,因此系统效率较高。
OLAP的概念,在实际应用中存在广义和狭义两种不同的理解。广义上的理解与字面意思相同,泛指一切不对数据进行更新的分析处理,但更多的情况下OLAP被理解为狭义上的含义,即与多维分析相关,是基于立方体(CUBE)计算而进行的分析。
BI(Business Intelligence),即商务智能,是指用现代数据仓库技术、在线分析技术、数据挖掘和数据展现技术进行数据分析以实现商业价值。
如今,许多企业已经建立了自己的数据仓库,用于存放和管理不断增长的数据,这些数据中蕴含着丰富的商业价值,但只有使用分析工具对其进行大量筛选、计算和展示后,数据中蕴含的规律、价值和潜在信息才能被人们所发现与利用。分析人员结合这些信息进行商业决策和市场活动,从而为用户提供更好的服务,为企业创造更大的价值。
2.1.2 维度建模
维度建模用于决策制定,并侧重于业务如何表示和理解数据。基本的维度模型由维度和度量两类对象组成。维度建模尝试以逻辑、可理解的方式呈现数据,以使得数据的访问更加直观。维度设计的重点是简化数据和加快查询。
维度模型是数据仓库的核心。它经过精心设计和优化,可以为数据分析和商业智能(BI),检索并汇总大量的相关数据。在数据仓库中,数据修改仅定期发生,并且是一次性开销,而读取是经常发生的。对于一个数据检索效率比数据处理效率重要得多的数据结构而言,非标准化的维度模型是一个不错的解决方案。
在数据挖掘中有几种常见的多维数据模型,如星形模型(Star Schema)、雪花模型(Snowflake Schema)、事实星座模型(Fact Constellation)等。
星形模型中有一个事实表,以及零个或多个维度表,事实表与维度表通过主键外键相关联,维度表之间没有关联,就像很多星星围绕在一个恒星周围,故名为星形模型。
如果将星形模型中的某些维度表再做规范,抽取成更细的维度表,让维度表之间也进行关联,那么这种模型称为雪花模型。
事实星座模型是更为复杂的模型,其中包含多个事实表,而维度表是公用的,可以共享。
2.1.3 事实表和维度表
事实表(Fact Table)是指存储事实记录的表,如系统日志、销售记录等,并且是维度模型中的主表,代表着键和度量的集合。事实表的记录会不断地动态增长,所以它的体积通常远大于其他表,通常事实表占据数据仓库中90%或更多的空间。
维度表(Dimension Table),也称维表或查找表(Lookup Table),是与事实表相对应的一种表。维度表的目的是将业务含义和上下文添加到数据仓库中的事实表和度量中。维度表是事实表的入口点,维度表实现了数据仓库的业务接口。它们基本上是事实表中的键引用的查找表。它保存了维度的属性值,可以与事实表做关联,相当于将事实表上经常出现的属性抽取、规范出来用一张表进行管理,常见的维度表有:日期表(存储日期对应的 周、月、季度等属性)、地点表(包含国家、省/州、城市等属性)等。使用维度表的好处如下:
- 减小了事实表的大小;
- 便于维度的管理和维护,增加、删除和修改维度的属性时,不必对事实表的大量记录进行改动;
- 维度表可以为多个事实表同时使用,减少重复工作。
2.1.4 维度和度量
维度和度量是数据分析中的两个基本概念。
维度是人们观察数据的特定角度,是考虑问题时的一类属性。它通常是数据记录的一个特征,如时间、地点等。同时,维度具有层级概念,可能存在细节程度不同的描述方面,如日期、月份、季度、年等。
在数据仓库中,可以在数学上求和的事实属性称为度量。例如,可以对度量进行总计、平均、以百分比形式使用等。度量是维度模型的核心。通常,在单个查询中检索数千个或数百万个事实行,其中对结果集执行数学方程。
在一个SQL查询中,Group By的属性通常就是维度,而其所计算的值则是度量,如在下面这个查询中,part_dt和lstg_site_id是维度,sum(price)和count(distinct seller_id)是度量。
select part_dt, lstg_site_id, sum(price) as total_selled, count(distinct seller_id)
as sellers from kylin_sales group by part_dt, lstg_site_id
2.1.5 Cube、Cuboid和Cube Segment
Cube(或称Data Cube),即数据立方体,是一种常用于数据分析与索引的技术,它可以对原始数据建立多维度索引,大大加快数据的查询效率。
Cuboid特指Apache Kylin中在某一种维度组合下所计算的数据。
Cube Segment指针对源数据中的某一片段计算出来的Cube数据。通常,数据仓库中的数据数量会随时间的增长而增长,而Cube Segment也是按时间顺序构建的。
2.2 在Hive中准备数据
上一节介绍了Apache Kylin中的常见概念。在本节中将介绍准备Hive数据时的一些注意事项。需要进行分析的数据必须先保存为Hive表的形式,只有这样Apache Kylin才能从Hive中导入数据、创建Cube。
Apache Hive是一个基于Hadoop的数据仓库工具,最初由Facebook开发并贡献到Apache软件基金会。Hive可以将结构化的数据文件映射为数据库表,并可以将SQL语句转换为MapReduce或Tez任务运行,从而让用户以类SQL(HiveQL,HQL)的方式管理和查询Hadoop上的海量数据。
此外,Hive提供了多种方式(如命令行、API和Web服务等)供第三方方便地获取和使用元数据并进行查询。今天,Hive已经成为Hadoop数据仓库的首选,是Hadoop不可或缺的一个重要组件,很多项目都兼容或集成Hive。鉴于此,Apache Kylin选择Hive作为原始数据的主要来源。
在Hive中准备待分析的数据是使用Apache Kylin的前提。将数据导入Hive表的方法很多,用户管理数据的技术和工具也多种多样,故其具体步骤不在本书的讨论范围之内,如有需要可以参阅Hive的文档。这里着重阐述几个需要注意的事项。
2.2.1 多维数据模型
目前Apache Kylin既支持星形数据模型,也支持雪花数据模型,这是基于以下考虑:
星形模型与雪花模型是最为常用的数据模型;
由于只有一个大表,相比于其他模型更适合大数据处理;
其他模型可以通过一定的转换,变为星形模型或雪花模型。
2.2.2 维度表的设计
除了数据模型以外,Apache Kylin还对维度表有一定的要求,如:
1)要具有数据一致性。主键值必须唯一,Apache Kylin会进行检查,如果有两行数据的主键相同,则系统就会报错。
2)维度表越小越好。Apache Kylin支持选择是否将维度表加载到内存中以供查询,过大的表不适合作为维度表,默认的阈值是300Mb。
3)改变频率低。Apache Kylin会在每次构建中试图重用维度表快照,如果维度表经常改变的话,重用就会失效,这会导致要经常对维度表创建快照。
4)维度表最好不是Hive视图(View),虽然在Apache Kylin v1.5.3中加入了对维度表是视图的支持,但每次都需要将视图物化,导致额外的时间成本。
2.2.3 Hive表分区
Hive表支持多分区(partition)。简单来说,一个分区就是一个文件目录,存储了特定的数据文件。当有新的数据生成的时候,可以将数据加载到指定的分区,读取数据的时候也可以指定分区。对于SQL查询,如果查询中指定了分区列的属性条件,则Hive会智能地选择特定分区(目录),从而避免全量数据的扫描,减少读写操作对集群的压力。
下面的一组SQL语句,演示了如何使用分区:
Hive> create table invites (id int, name string) partitioned by (ds string) row format
delimited fields terminated by 't' stored as textfile;?
Hive> load data local inpath '/user/hadoop/data.txt' overwrite into table invites
partition (ds='2016-08-16');?
Hive> select * from invites where ds ='2016-08-16';
Apache Kylin支持增量的Cube构建,通常是按时间属性来增量地从Hive表中抽取数据。如果Hive表正好是按此时间属性做分区的话,那么可以利用到Hive分区的好处,每次Hive构建的时候可以直接跳过不相干日期的数据,节省Cube构建的时间。这样的列在Apache Kylin里也称为分割时间列(partition time column),通常它应该也是Hive表的分区列。
2.2.4 了解维度的基数
维度的基数(Cardinality)指的是该维度在数据集中出现的不同值的个数。例如,“国家”是一个维度,有200个不同的值,那么此维度的基数是200。通常,一个维度的基数为几十到几万,个别维度如“用户ID”的基数会超过百万甚至千万,基数超过一百万的维度通常被称为超高基数维度(Ultra High Cardinality,UHC),需要引起设计者的注意。
Cube中所有维度的基数可以体现Cube的复杂度,如果一个Cube中有多个超高基数维度,那么这个Cube膨胀的几率就会很高。在创建Cube前对所有维度的基数做一个了解,可以帮助设计合理的Cube。计算基数有多种途径,最简单的方法就是让Hive执行一个count distinct的SQL查询,同时Apache Kylin也提供了计算基数的方法,这部分内容会在2.4.1节中进行介绍。
2.2.5 样例数据
如果需要一些简单数据来快速体验Apache Kylin,也可以使用Apache Kylin自带的样例数据。运行“${KYLIN_HOME}/bin/sample.sh”来导入样例数据,然后就能继续按下面的流程创建模型和Cube。下面的示例和配图都是基于样例数据制作的。
2.3 安装和启动Apache Kylin
如果数据已经在Hive中准备好,并已经满足2.2节介绍的条件,那么就可以开始安装和使用Apache Kylin了。本节将介绍Apache Kylin的安装环境及启动方法。
2.3.1 环境准备
众所周知,Apache Kylin依赖于Hadoop集群处理大量数据集。因此在安装Apache Kylin之前必须准备Hadoop环境。由于Apach Hadoop版本管理混乱,推荐安装Cloudera CDH或Hortonworks HDP等商业Hadoop发行版。
2.3.2 必要组件
准备好Hadoop环境之后,还需要安装一些应用以支持Apache Kylin的分析查询,其中必不可少的有YARN、HDFS、MapReduce、Hive、HBase、Zookeeper和其他一系列服务以保证Apache Kylin的运行稳定可靠。
2.3.3 启动Apache Kylin
一切准备就绪之后,就能够启动Apache Kylin了。首先从Apache Kylin下载一个适用Hadoop版本的二进制文件,解压相应的二进制文件,并配置环境变量KYLIN_HOME指向Kylin文件夹。之后运行“$KYLIN_HOME/bin/kylin.sh start”脚本启动Apache Kylin。
Apache Kylin 启动后,可以通过浏览器“http://:7070/kylin”进行访问。其中,“”为具体的机器名、IP 地址或域名,默认端口为“7070”,初始用户名和密码为“ADMIN/KYLIN”。服务器启动后,您可以通过查看“$KYLIN_HOME/logs/kylin.log”获得运行日志。
2.4 设计Cube
如果数据已经在Hive中准备好,并已经满足2.3节介绍的条件,那么就可以开始设计和创建Cube了,本节将按常规的步骤介绍Cube是如何创建的。
2.4.1 导入Hive表定义
登录Apache Kylin的Web界面,创建新的或选择一个已有项目后,需要做的就是将Hive表的定义导入Apache Kylin。
点击 Web界面的“Model”→“Data source”下的“Load Hive Table Metadata”图标,然后输入表的名称(可以一次导入多张表,以逗号分隔表名)(如图2-1所示),点击按钮“Sync”,Apache Kylin就会使用Hive的API从Hive中获取表的属性信息。
导入成功后,表的结构信息会以树状形式显示在页面的左侧,可以点击展开或收缩,如图2-2所示。
同时,Apache Kylin会在后台触发一个MapReduce任务,计算此表每个列的基数。通常稍过几分钟后刷新页面,就会看到基数信息显示出来,如图2-3所示。
需要注意的是,这里Apache Kylin对基数的计算采用的是HyperLogLog的近似算法,与精确值略有误差,但作为参考值已经足够。
2.4.2 创建数据模型
有了表信息后,就可以开始创建数据模型了。数据模型(Data Model)是Cube的基础,主要根据分析需求进行设计。有了数据模型以后,定义Cube的时候就可以直接从此模型定义的表和列中进行选择了,省去了重复指定连接(JOIN)条件的步骤。基于一个数据模型可以创建多个Cube,方便减少用户的重复性工作。
在Apache Kylin界面的“Model”页面,点击“New”→“New Model”命令,开始创建数据模型。给模型输入名称后,选择一个事实表(必需的),然后添加维度表(可选),如图2-4所示。
添加维度表的时候,首先选择表之间的连接关系,同时选择表之间的连接类型:是inner jion还是left jion,并为创建的维度表输入别名。同时可以选择是否将其以快照(Snapshot)形式存储到内存中以供查询。当维度表小于300MB时,推荐启用维度表以快照形式存储,以简化Cube计算和提高系统整体效率。当维度表超过300MB上限时,则建议关闭维度表快照,以提升Cube构建的稳定性与查询的性能。然后选择连接的主键和外键,这里也支持多主键,如图2-5所示。
接下来选择用作维度和度量的列。这里只是选择一个范围,不代表这些列将来一定要用作 Cube的维度或度量,你可以把所有可能会用到的列都选进来,后续创建Cube的时候,将只能从这些列中进行选择。
选择维度列时,维度可以来自事实表或维度表,如图2-6所示。
选择度量列时,度量只能来自事实表或不加载进内存的维度表,如图2-7所示。
最后一步,是为模型补充分割时间列信息和过滤条件。如果此模型中的事实表记录是按时间增长的,那么可以指定一个日期/时间列作为模型的分割时间列,从而可以让Cube按此列做增量构建,关于增量构建的具体内容参见第4章。
过滤(Filter)条件是指,如果想把一些记录忽略掉,那么这里可以设置一个过滤条件。Apache Kylin在向Hive请求源数据的时候,会带上此过滤条件。如图2-8所示,会只保留金额(price)大于0的记录。
最后,点击“Save”保存此数据模型,随后它将出现在“Model”的列表中。
2.4.3 创建Cube
本节简单介绍了创建Cube时的各种配置选项,但是由于篇幅限制,这里没有对Cube的配置和优化进行进一步展开介绍。读者可以在后续的章节(如第3章“Cube优化”)中找到关于Cube的配置和优化的更详细的介绍。接下来开始Cube的创建。点击“New”→“New Cube”命令会开启一个包含若干步骤的向导。
第一步,选择要使用的数据模型,并为此Cube输入一个唯一的名称(必需的)和描述(可选)(如图2-9所示);这里还可以输入一个邮件通知列表,以在构建完成或出错时收到通知。如果不想接收在某些状态的通知,可以从“Notification Events”中将其去掉。
第二步,添加Cube的维度。点击“Add Dimension”按钮添加维度,Apache Kylin会用一个树状结构呈现出所有列,用户只需勾选想要的列即可,同时需要为每个维度输入名字,可以设定是普通维度或是衍生(Derived)维度(如图2-10所示)。如果被设定为衍生维度的话,由于这些列值都可以从该维度表的主键值中衍生出来,所以实际上只有主键列会被Cube加入计算。而在Apache Kylin的具体实现中,往往采用事实表上的外键替代主键进行计算和存储。但是逻辑上可以认为衍生列来自维度表的主键。
第三步,创建度量。Apache Kylin支持的度量有SUM、MIN、MAX、COUNT、COUNT_DISTINCT、TOP_N、EXTENDED_COLUMN、PERCENTILE等。默认Apache Kylin会创建一个Count(1)度量。
可以通过点击“Bulk Add Measure”按钮批量添加度量。目前对于批量添加度量,Apache Kylin只支持SUM、MIN、MAX等简单函数。只需要选择度量类型,然后再选择需要计算的列,如图2-11所示。
如果需要添加复杂度量,可以点击“+Measure”按钮来添加新的度量。请选择需要的度量类型,然后再选择适当的参数(通常为列名)。图2-12所示为一个SUM(price)的示例。
重复以上操作,创建所需要的度量。Apache Kylin可以支持在一个Cube中有上百个的度量,添加完所有度量后,点击“Next”按钮,如图2-13所示。
第四步,进行关于Cube数据刷新的设置(如图2-14所示)。在这里可以设置自动合并的阈值、自动合并触发时保留的阈值、数据保留的最小时间,以及第一个Segment的起点时间(如果Cube有分割时间列),详细内容请参考第4章。
第五步,高级设置。在此页面可以设置维度聚合组和Rowkey属性。
默认Apache Kylin会把所有维度放在同一个聚合组(Aggregation Group,也称维度组)中,如果维度数较多(如>15),建议用户根据查询的习惯和模式,点击“New Aggregation Group+”命令,将维度分布到多个聚合组中。通过使用多个聚合组,可以大大降低Cube中的Cuboid数量。
举例说明,一个Cube有(M+N)个维度,如果把这些维度都放置在一个组里,那么默认会有2(M+N)个Cuboid;如果把这些维度分为两个不相交的聚合组,第一个组有M个维度,第二个组有N个维度,那么Cuboid的总数量将被减至2M + 2N,比之前的2(M+N)极大地减少了。
在单个聚合组中,可以对维度设置一些高级属性,如Mandatory Dimensions、Hierarchy Dimensions、Joint Dimensions等。这几种属性都是为优化Cube的计算而设计的,了解这些属性的含义对于更好地使用Cube至关重要。
强制维度(Mandatory Dimensions):指的是那些总是会出现在Where条件或 Group By语句里的维度。通过指定某个维度为强制维度,Apache Kylin可以不预计算那些不包含此维度的Cuboid,从而减少计算量。
层级维度(Hierarchy Dimensions):是指一组有层级关系的维度,如“国家”“省”“市”,这里“国家”是高级别的维度,“省”“市”依次是低级别的维度。用户会按高级别维度进行查询,也会按低级别维度进行查询,但当查询低级别维度时,往往会带上高级别维度的条件,而不会孤立地审视低维度的数据。例如,用户会点击“国家”作为维度来查询汇总数据,也可能点击“国家”+“省”,或者“国家”+“省”+“市”来进行查询,但是不会跨越“国家”直接点击“省”或“市”来进行查询。通过指定层级维度,Apache Kylin可以略过不满足此模式的Cuboid。
联合维度(Joint Dimensions):是将多个维度视作一个维度,在进行组合计算的时候,它们要么一起出现,要么均不出现,通常适用于以下几种情形:
- 总是一起查询的维度;
- 彼此之间有一定映射关系,如 USER_ID 和 EMAIL;
- 基数很低的维度,如性别、布尔类型的属性。
如图2-15所示,先通过在“Includes”中选择要添加的维度到本聚合组中,然后根据模型特征和查询模式,设置高级维度属性。“Hierarchy Dimensions”和“Joint Dimensions”可以设置多组,但要注意,一个维度出现在某个属性中后,将不能再设置另一种属性。但是一个维度,可以出现在多个聚合组中。
在Apache Kylin中是以Key-Value的形式将Cube的构建结果存储到Apache HBase中的。我们知道,HBase 是一种单索引、支持超宽表的数据存储引擎。HBase的Rowkey,即行键是用来检索其他列的唯一索引。Apache Kylin需要按照多个维度来对度量进行检索,因此在存储到HBase的时候,需要将多个维度值进行拼接组成Rowkey。图2-16中介绍了Apache Kylin将Cube存储在 HBase中的原理。
由于同一维度中的数值长短不一,如国家名,短的如“中国”,长的如“巴布亚新几内亚”,因此将多个不同列的值进行拼接的时候,要么添加分隔符,要么通过某种编码使各个列所占的宽度固定。Apache Kylin为了能够在HBase上高效地进行存储和检索,会使用第二种方式对维度值进行编码。维度编码的优势如下:
- 压缩信息存储空间;
- 提高扫描效率,减少解析开销。
编码(Encoding)代表了该维度的值使用何种方式进行编码,默认采用字典(Dictionary)编码技术。而合适的编码能够减少维度对空间的占用。例如,我们可以把所有的日期用三个字节进行编码,相比于使用字符串,或者使用长整数形式进行存储,我们的编码方式能够大大减少每行Cube数据的体积。而Cube中可能存在数以亿计的行,累加起来使用编码节约的空间将是非常庞大的。
目前Apache Kylin支持的编码方式有以下几种。
- Dictionary编码:字典编码是将所有此维度下的值构建成一张映射表,从而大大节约存储空间。另外,字典是保持顺序的,这样可以使得在HBase中进行比较查询的时候,依然使用编码后的值,而无须解码。Dictionary的优势是,产生的编码非常紧凑,尤其在维度的值基数小且长度大的情况下,Dictionary编码特别节约空间。由于产生的字典在使用时加载进构建引擎和查询引擎,所以在维度的基数大、长度也大的情况下,容易造成构建引擎或者查询引擎的内存溢出。在Apache Kylin中,字典编码允许的基数上限默认是500万(由其参数“kylin.dictionary.max.cardinality”配置)。
- Date编码:将日期类型的数据使用三个字节进行编码,支持从0000-01-01到9999-01-01中的每一个日期。
- Time编码:仅支持表示从1970-01-01 00:00:00到 2038-01-19 03:14:07的时间,且Timestamp类型的维度经过编码和反编码之后,会失去毫秒信息,所以说Time编码仅仅支持到秒。但是Time编码的优势是每个维度仅使用四个字节,相比普通的长整数编码节约了一半空间。如果能够接受秒级的时间精度,可以选择Time来编码代表时间的维度。
- Integer编码:Integer编码适合于对int或bigint类型的值进行编码,它无须额外存储,同时可以支持很大的基数。使用时需要提供一个额外的参数“Length”来代表需要多少个字节。“Length”的长度为1~8。如果用来编码int32类型的整数,可以将“Length”设为“4”;如果用来编int64类型的整数,可以将“Length”设为“8”。在多数情况下,如果我们知道一个整数类型维度的可能值都很小,那么就能使用“Length”为“2”甚至是“1”的int编码来存储,这能够有效避免存储空间的浪费。
- Fixed_length编码:该编码需要提供一个额外的参数“Length”来代表需要多少个字节。对于基数大、长度也大的维度来说,使用Dict可能不能正常执行,于是可以采用一段固定长度的字节来存储代表维度值的字节数组,该数组为字符串形式的维度值的UTF-8字节。如果维度值的长度大于预设的Length,那么超出的部分将会被截断。此编码方式其实只是将原始值截断或补齐成相同长度的一组字节,没有额外的转换,所以空间效率较差,通常只是一种权宜手段。
在未来,Apache Kylin还有可能为特定场景、特定类型的维度量身定制特别的编码方式,如在很多行业,身份证号码可能是一个重要的维度。但是身份证号码由于其特殊性而不能使用整数类型的编码(身份证号码的最后一位可能是X),其高基数的特点也决定了其不能使用Dict编码,在目前的版本中只能使用Fixed_length编码,但显然Fixed_length不能充分利用身份证号码中大部分字节是数字的特性来进行深度编码,因此存在一定程度的存储空间的浪费。
同时,各个维度在Rowkey中的顺序,也会对查询的性能产生较明显的影响。在这里用户可以根据查询的模式和习惯,通过拖曳的方式调整各个维度在Rowkey上的顺序(如图2-17所示)。一般原则是,将过滤频率高的列放置在过滤频率低的列之前,将基数高的列放置在基数低的列之前。这样做的好处是,充分利用过滤条件来缩小在HBase中扫描的范围,从而提高查询的效率。
在构建Cube时,可以通过维度组合白名单(Mandatory Cuboids)确保想要构建的Cuboid能被成功构建(如图2-18所示)。
Apache Kylin支持对于复杂的COUNT DISTINCT度量进行字典构建,以保证查询性能。目前提供两种字典格式,即Global Dictionary和Segment Dictionary(如图2-19所示)。
其中,Global Dictionary可以将一个非integer的值转成integer值,以便bitmap进行去重,如果你要计算COUNT DISTINCT的列本身已经是integer类型,那就不需要定义Global Dictionary。并且Global Dictionary会被所有segment共享,因此支持跨segments做上卷去重操作。
而Segment Dictionary虽然也是用于精确计算 COUNT DISTINCT的字典,但与Global Dictionary不同的是,它是基于一个segment的值构建的,因此不支持跨segments的汇总计算。如果你的cube不是分区的或者能保证你的所有SQL按照partition column进行group by, 那么最好使用Segment Dictionary而不是Global Dictionary,这样可以避免单个字典过大的问题。
Apache Kylin目前提供的Cube构建引擎有两种:MapReduce和Spark(如图2-20所示)。如果你的 Cube只有简单度量(如SUM、 MIN、MAX),建议使用Spark。如果Cube中有复杂类型度量(如COUNT DISTINCT、 TOP_N),建议使用MapReduce。
为了提升构建性能,你可以在Advanced Snapshot Table中将维表设置为全局维表,同时提供不同的存储类型(如图2-21所示)。
在构建时 Apache Kylin允许在Advanced Column Family中对度量进行分组(如图2-22所示)。如果有超过一个的 COUNT DISTINCT 或 Top_N 度量, 你可以将它们放在更多列簇中,以优化与HBase 的I/O。
第五步,为Cube配置参数。和其他Hadoop工具一样,Apache Kylin使用了很多配置参数,用户可以根据具体的环境、场景等配置不同的参数进行灵活调优。Apache Kylin全局的参数值可以在conf/kylin.properties文件中进行配置;如果Cube需要覆盖全局设置的话,需在此页面指定。点击“+Property”按钮,然后输入参数名和参数值,如图2-23所示,指定“kylin.hbase.region.cut”的值为“1”,这样,此Cube在存储的时候,Apache Kylin将会按每个HTable Region存储空间为1GB来创建HTable Region。如果用户希望任务从YARN中获取更多内存,可以设置kylin.engine.mr.config-override.mapreduce.map.memory.mb、kylin.engine.mr.config-override.mapreduce.map.java.opts 等mapreduce相关参数。如果用户希望Cube的构建任务使用不同的YARN 资源队列,可以设置kylin.engine.mr.config-override.mapreduce.job.queuename。这些配置均可以在Cube级别重写。
然后点击“Next”按钮到最后一个确认页面,如有修改,点“Prev”按钮返回进行修改,最后点“Save”按钮进行保存,一个Cube就创建完成了。创建好的Cube会显示在“Cubes”列表中,如要对Cube的定义进行修改,只需点“Edit”按钮就可以修改。也可以展开此Cube行以查看更多信息,如JSON格式的元数据、访问权限、通知列表等。
2.5 构建Cube
本节简单地介绍了构建Cube的相关操作说明和设置,受篇幅的限制许多具体内容没有深入展开,读者可以从第3章“Cube优化”和第4章“增量构建”中获得更详细的介绍。
新创建的Cube只有定义,而没有计算的数据,它的状态是“DISABLED”,是不会被查询引擎挑中的。要想让Cube有数据,还需对它进行构建。Cube的构建方式通常有两种:全量构建和增量构建,两者的构建步骤是完全一样的,区别只在于构建时读取的数据源是全集还是子集。
Cube的构建包含以下步骤,由任务引擎调度执行:
1)创建临时的Hive平表(从Hive中读取数据);
2)计算各维度的不同值,并收集各Cuboid的统计数据;
3)创建并保存字典;
4)保存Cuboid统计信息;
5)创建HTable;
6)计算Cube (一轮或若干轮计算);
7)将Cube计算结果转成HFile;
8)加载HFile到HBase;
9)更新Cube元数据;
10)垃圾回收。
上述步骤中,前五步是为计算Cube而做的准备工作,如遍历维度值来创建字典,对数据做统计和估算以创建HTable等。第六步是真正的Cube计算,取决于使用的Cube算法,它可能是一轮MapReduce任务,也可能是N(在没有优化的情况下,N可以被视作维度数)轮迭代的MapReduce。
由于Cube运算的中间结果是以SequenceFile的格式存储在HDFS上的,所以为了导入HBase,还需要进行第七步操作,将这些结果转换成HFile(HBase文件存储格式)。第八步通过使用HBase BulkLoad工具,将HFile导入HBase集群,这一步完成后,HTable就可以查询到数据。第九步更新Cube的数据,将此次构建的Segment的状态从“NEW”更新为“READY”,表示已经可供查询。最后一步,清理构建过程中生成的临时文件等垃圾,释放集群资源。
Monitor页面会显示当前项目下近期的构建任务。图2-24中显示了一个正在运行的Cube构建任务,当前进度46.67%。
点击任务右边的“>”按钮,可以将其展开得到该任务每一步的详细信息,如图2-25所示。
如果Cube构建任务中的某一步骤是执行Hadoop任务的话,会显示Hadoop任务的链接,点击即可跳转到Hadoop对应的任务监测页面,如图2-26所示。
如果任务执行中的某一步骤报错,任务引擎会将任务状态置为“ERROR”并停止后续操作的执行,等待用户排错。在错误排除后,用户可以点击“Resume”从上次报错的位置恢复执行。或者如果需要修改Cube或重新开始构建,用户需点击“Discard”来放弃此次构建。
接下来介绍几种不同的构建方式。
2.5.1 全量构建和增量构建
1. 全量构建
对数据模型中没有指定分割时间列信息的Cube,Apache Kylin会采用全量构建,即每次都从Hive中读取全部的数据来开始构建。通常它适用于以下两种情形:
- 事实表的数据不是按时间增长的;
- 事实表的数据比较小或更新频率很低,全量构建不会造成太大的存储空间浪费。
2. 增量构建
进行增量构建的时候,Apache Kylin每次都会从Hive中读取一个时间范围内的数据,然后对其进行计算,并以一个Segment的形式保存。下次构建的时候,自动以上次结束的时间为起点时间,再选择新的终止时间进行构建。经过多次构建后,Cube中会有多个Segment依次按时间顺序进行排列,如 Seg-1, Seg-2,…,Seg-N。进行查询的时候,Apache Kylin会查询一个或多个Segment然后再做聚合计算,以便返回正确的结果给请求者。
使用增量构建的优势是,每次只需要对新增数据进行计算,避免了对历史数据进行重复计算。对于数据量很大的Cube,使用增量构建是非常有必要的。
图2-27所示为构建一个Segment的Cube的输入框,需要用户选择时间范围。
在从Hive中读取源数据的时候,Apache Kylin会带上此时间条件,如图2-28所示。
增量构建抽取数据的范围,采用前包后闭原则,也即包含开始时间,但不包含结束时间,从而保证上一个Segment的结束时间与下一个Segment的起始时间相同,但数据不会重复。
如果使用Apache Kylin的Web GUI触发,起始时间会被自动填写,用户只需选择结束时间。如果使用Rest API触发,用户则需确保时间范围不会与已有的Segment重合。
2.5.2 历史数据刷新
Cube构建完成以后,如果某些历史数据发生了变动,需要针对相应的Segment重新进行计算,这种构建称为刷新。刷新通常只针对增量构建的Cube而言,因为全量构建的Cube只要重新全部构建就可以得到更新;而增量更新的Cube因为有多个Segment,需要先选择要刷新的Segment,然后再进行刷新。
图2-29所示为提交刷新的请求页面,用户需要在下拉列表中选择一个时间区间。
提交刷新请求以后,生成的构建任务与最初的构建任务完全相同。
在刷新的同时,Cube仍然可以被查询,只是返回的是陈旧数据。当Segment刷新完毕后,新Segment会立即生效,查询开始返回最新的数据。原Segment则成为垃圾,等待回收。
2.5.3 合并
随着时间的迁移,Cube中可能存在较多数量的Segment,使得查询性能下降,并且会给HBase集群管理带来压力。对此,需要适时地做Segment的合并,将若干个小Segment合并成较大的Segment。
合并有如下优势:
- 合并相同的Key,从而减少Cube的存储空间;
- 由于Segment减少,可以减少查询时的二次聚合,提高了查询性能;
- HTable数量得以减少,便于集群的管理。
下面来看看合并的操作步骤,图2-30中的Cube有两个Segment。
现在触发一个合并,点击“Actions” →“Merge”;选择要合并的起始Segment和结束Segment,生成一个合并的任务,如图2-31所示。
进行合并的时候,Apache Kylin会直接以最初各个Segment构建时生成的Cuboid文件作为输入内容,不需要从Hive中加载原始数据。后续的步骤跟构建时基本一致。直到新的HTable加载完成,Apache Kylin才会卸载原来的HTable,以确保在整个合并过程中,Cube都是可以查询的。
合并完成后,此Cube的Segment减少为1个,如图2-32所示。
2.6 查询Cube
本节简要介绍如何查询Cube。更多内容请参考后续章节(如第5章“查询与可视化”)。
Cube构建好以后,状态变为“READY”,就可以进行查询了。Apache Kylin的查询语言是标准SQL的SELECT语句,这是为了获得与大多数BI系统和工具无缝集成的可能性。一般的查询语句类似以下SQL语句:
SELECT DIM1, DIM2, …, MEASURE1, MEASURE2… FROM FACT_TABLE
INNER JOIN LOOKUP_1 ON FACT_TABLE.FK1 = LOOKUP_1.PK
INNER JOIN LOOKUP_2 ON FACT_TABLE.FK2 = LOOKUP_2.PK
WHERE FACT_TABLE.DIMN = ‘’ AND …
GROUP BY DIM1, DIM2…
需要了解的是,只有当查询的模式跟Cube定义相匹配的时候,Apache Kylin才能够使用Cube的数据来完成查询。“Group By”的列和“Where”条件里的列,必须是在维度中定义的列,而SQL中的度量,应该跟Cube中定义的度量一致。
在一个项目下,如果有多个基于同一模型的Cube,而且它们都满足查询对表、维度和度量的要求,Apache Kylin会挑选一个“最优的”Cube来进行查询。这是一种基于成本(cost)的选择,Cube的成本计算涉及多方面因素,如Cube的维度数、度量、数据模型的复杂度等。
如果查询是在Apache Kylin的Web GUI上进行的,查询结果会以表的形式展现,如图2-33所示。所执行的Cube名称也会一同显示。用户可以点击“Visualization”按钮生成简单的可视化图形,或点击“Export”按钮下载结果集到本地。
2.6.1 Apache Kylin查询介绍
Apache Kylin使用Apache Calcite做SQL语法分析,并且Apache Kylin深度定制了Calcite。Apache Calcite是一个开源的SQL引擎,它提供了标准SQL语言解析、多种查询优化和连接各种数据源的能力。Calcite项目在Hadoop中越来越引人注目,并被众多项目集成为SQL解析器。
在Apache Kylin一条查询的执行过程主要分成四个部分:词法分析、逻辑执行计划、物理执行计划和执行。以如下SQL语句为例:
SELECT TEST_CAL_DT.WEEK_BEG_DT, SUM(TEST_KYLIN_FACT.PRICE) FROM TEST_KYLIN_FACT AS FACT
INNER JOIN EDW.TEST_CAL_DT as DT ON FACT.CAL_DT = DT.CAL_DT
WHERE FACT.CAL_DT > ‘2017-01-01’
GROUP TEST_CAL_DT.WEEK_BEG_DT
在词法分析阶段,Calcite将该查询拆分成包含关键词识别的字符段,如图2-34所示。
之后,Calcite根据词法分析的结果,生成一个逻辑执行计划,如图2-35所示。
之后,Calcite会基于规则对逻辑执行计划进行优化,在优化的过程中根据这些规则将算子转化为对应的物理执行算子。而Apache Kylin则在其中增加了一些优化策略。首先,在每一个优化规则中将对应的物理算子转换成Apache Kylin自己的OLAPxxxRel算子。然后根据每一个算子中保持的信息构造本次查询的上下文OLAPContext。之后再根据本次查询中使用的维度列、度量信息等查询是否有满足本次查询的Cuboid,如果有则将其保存在OLAPContext的realization中,如图2-36所示。
之后Calcite会根据这个执行计划动态生成执行代码。并且根据之前记录在OLAPContext中的realization信息,到HBase中读取相对应的已经构建好的cuboid数据,用以回答查询,如图2-37所示。
2.6.2 查询下压
在Apache Kylin中的查询,只有预先针对查询内的维度和度量进行建模并构建Cube才能够回答查询。因此在Apache Kylin中针对于无法击中Cube的查询,便有了另外一种处理方式即查询下压。查询下压的本质是将无法用Cube回答的查询路由到Hive或Spark这类查询引擎,用以回答该查询。查询下压的实现方式如图2-38所示。
一条查询经过解析后,进入查询路由,首先会进入Cube查询执行器中去寻找是否有能够回答该查询的Cube。如果没有找到合适的Cube,则会抛出异常“No realization found.”,并将这个结果抛回查询路由,查询路由检测到该异常后,则会将该查询路由到一个外部查询引擎(如Hive),以回答这条查询。
2.7 SQL参考
Apache Kylin支持标准SQL查询语言,但是SQL有很多变体,Apache Kylin支持的只是SQL所有变体中的一个子集,并不是支持所有现存的SQL语句和语法。用户在使用Apache Kylin之前,需要对Apache Kylin的SQL支持有一个了解,避免走弯路。
首先,Apache Kylin作为OLAP引擎,只支持查询,而不支持其他操作,如插入、更新等,即所有SQL都必须是SELECT语句,否则Apache Kylin会报错。
第二,在Apache Kylin中进行查询时,使用SQL语句中的表名、列名、度量、连接关系等条件,来匹配数据模型和Cube;在设计Cube的时候,就要充分考虑查询的需求,避免遗漏表、列等信息。
第三,进行查询时一条SQL需要首先被Apache Calcite解析,然后才可以被Apache Kylin执行。下面是Calcite中的SELECT语句的语法:
SELECT [ STREAM ] [ ALL | DISTINCT ]
{ * | projectItem [, projectItem ]* }
FROM tableExpression
[ WHERE booleanExpression ]
[ GROUP BY { groupItem [, groupItem ]* } ]
[ HAVING booleanExpression ]
[ WINDOW windowName AS windowSpec [, windowName AS windowSpec ]* ]
projectItem:
expression [ [ AS ] columnAlias ]
| tableAlias . *
tableExpression:
tableReference [, tableReference ]*
| tableExpression [ NATURAL ] [ LEFT | RIGHT | FULL ] JOIN tableExpression
[ joinCondition ]
joinCondition:
ON booleanExpression
| USING '(' column [, column ]* ')'
2.8 小结
本章介绍了使用Apache Kylin前必须了解的基本概念,如星形数据模型、事实表、维表、维度、度量等,并在了解这些基本概念的基础上快速创建了基于Sample Data的模型,构建Cube,最后执行SQL查询。带领读者体验了Apache Kylin的主要使用过程。后续章节将继续展开和探讨这个过程中的一些关键技术,比如增量构建、可视化和Cube优化等。