Kylin

一、数仓回顾

1.1 核心概念

数据仓库,OLAP和OLTP,维度和度量,事实表和维度表。星形模型和雪花模型。

1.2 数据仓库

这是商业智能的核心部分,主要是将不同数据源的数据整合到一起,通过多维分析为企业提供决策支持报表生成等。存入数据仓库的资料必定包含时间属性。

数据仓库和数据库主要区别:

一般来说,在传统 BI 领域里,数据仓库的数据同样是存储在 MySQL 这样的数据库中。大数据领域最常用的数据仓库就是 Hive ,我们要学习的 Kylin 也是以 Hive 作为默认的数据源的。

1.3 OLAP和OLTP

OLAPOnline Analytical Process ),联机分析处理,大量历史数据为基础,配合时间点的差异,以多维度的方式分析数据, 一般带有主观的查询需求,多应用在数据仓库。

OLTP( Online Transaction Process ),联机事务处理,侧重于数据库的增删查改等常用业务操作

1.4 维度和度量

这两个是数据分析领域中两个常用的概念。维度简单来说就是你观察数据的角度,也就是记录数据的一个属性,例如时间、地点等。度量就是基于数据计算出来的考量值通常就是一个数据比如总销售额,不同的用户数量。我们就是从不同的维度来审查度量值,以便我们分析找出其中的变化规律。

对应我们的SQL查询,group  by的属性就是我们通常考量的维度,所计算出来的比如sum字段就是我们需要的度量。

1.5 Cube和Cuboid

我们确定好维度和度量之后,根据我们确定好的度量和维度可以构建cube(立方体)。也就是所谓的预计算,对原始数据建立的多维度索引。

给定一个数据模型,我们可以对其上的所有数据进行组合,对于N个维度来说,组合的所有可能性共有2^N种。对于每一种维度的组合,将度量做聚合运算,然后将运算的结果保存为一个物化视图,称为Cuboid 。所有维度组合的 Cuboid 作为一个整体,被称为Cube

所以简单来说,一个cube就是许多按维度聚合的物化视图的集合。

例子:

时间Time )、商品( Item )、地点( Location )和供应商( Supplier ),度量为销售额( GMV )。那么所有维度的组合就有 2^4 =16 16种(如下图所示),比如一维度1D )的组合有 [Time][Item][Location][Supplier] 4种; 二维度2D )的组合有 [Time, Item][TimeLocation][TimeSupplier][ItemLocation][ItemSupplier]、 [LocationSupplier] 6种;三维度3D )的 组合也有4种;最后零维度0D )和四维度4D )的 组合各有1种,总共就有 16种组合。下图为 kylin 官方图例。

通过维度聚合销售额来计算 cuboid ,通过 sql 语句来表达这样的计算 Cuboid[Time,Location] ,如下:

select Time,Location,SUM(GMV) as GMV from Sales group by Time,Location;

将计算的结构保存为物化视图,即所有的 Cuboid 物化视图的总称就是 Cube

1.5.1 Cube Segment

名知意代表的是元数据中的某一个片段计算出的 cube 数据。通常数据仓库中的数据量会随着时间的增长而增长,而 Cube Segment 也是按照时间顺序来构建的。

1.6 事实表和维度表

事实表Fact Table )是指存储有事实记录的表,如系统日志、销售记录等;事实表的记录在不断地动态增长,所以它的体积通常远大于其他表。

维度表Dimension Table )或维表,有时也称查找表( Lookup Table ),是与事实表相对应的一种表;它保存了维度的属性值,可以跟事实表做关联;相当于将事实表上经常重复出现的属性抽取、规范出来用一张表进行管理。

常见的维度表有:日期表(存储与日期对应的周、月、季度等的属性)、地点表(包含国家、省/州、城市等属性)等。使用维度表有诸多好处,具体如下:

缩小了事实表的大小。

便于维度的管理和维护,增加、删除和修改维度的属性,不必对事实表的大量记录进行改动。

维度表可以为多个事实表重用,以减少重复工作。

1.7 多维数据模型

1.7.1 星型模型

星型模型就是一张事实表,以及零个或多个维度表;事实表与维度表通过主键外键相关联,维度表之间没有关联,就像很多星星围绕在一个恒星周围,故取名为星形模型

 1.7.2 雪花模型

星形模型中的某些维表抽取成更细粒度的维表,然后让维表之间也进行关联,这种形状酷似雪花的的模型称为雪花模型

 二、Kylin简介

2.1 Kylin 的由来

Apache Kylin Extreme OLAP Engine for Big Data )是一个开源的分布式分析引擎,为 Hadoop 等大型分布式数据平台之上的超大规模数据集通过标准 SQL 查询及多维分析( OLAP )功能,提供亚秒级的交互式分析能力。

Apache Kylin ,中文名麒麟,是 Hadoop 动物园中的重要成员。 Apache Kylin 是一个开源的分布式分析引擎,最初由 eBay 开发贡献至开源社区。它提供 Hadoop 之上的 SQL 查询接口及多维分析 OLAP )能力以支持大规模数据,能够处理 TB 乃至 PB 级别的分析任务,能够在亚秒级查询巨大的Hive 表,并支持高并发。

2.2 Kylin的优势

Apache Kylin 的主要特点包括支持 SQL 接口、支持超大数据集、亚秒级响应、可伸缩性、高吞吐率、BI 工具集成等。

2.2.1 标准SQL接口

Apache Kylin 以标准 SQL 作为对外服务的主要接口: Kylin 使用的查询模型是数据源中的关系模型表,一般而言,也就是指 Hive 表。终端用户只需要像原来查询Hive表一样编写 SQL ,就可以无缝地切 换到 Kylin ,几乎不需要额外的学习,甚至原本的 Hive 查询也因为与 SQL 同源,大多都无须修改就能直接在 Kylin 上运行。

2.2.2 支持超大规模数据集

Apache Kylin 对大数据的支撑能力可能是目前所有技术中最为领先的。早在2015eBay 的生产环境中 Kylin 就能支持百亿记录的秒级查询,之后在移动的应用场景下又有了千亿记录秒级查询的案例。这些都是实际场景的应用,而非实验室中的理论数据。

因为使用了 Cube 预计算技术,在理论上, Kylin 可以支撑的数据集大小没有上限,仅受限于存储系统和分布式计算系统的承载能力,并且查询速度不会随数据集的增大而减慢。 Kylin 在数据集规模上的局限性主要在于维度的个数和基数。它们一般由数据模型来决定,不会随着数据规模的增长而线性增 长,这也意味着 Kylin 对未来数据的增长有着更强的适应能力。

2.2.3 亚秒级响应

Apache Kylin 拥有优异的查询响应速度,这点得益于预计算,很多复杂的计算,比如连接、聚合,在离线的预计算过程中就已经完成,这大大降低了查询时刻所需要的计算量,提高了响应速度。根据可查询到的公开资料可以得知, Apache Kylin 在某生产环境中90%的查询可以在3s内返回结果。这并不是 说一小部分 SQL 相当快,而是在数万种不同 SQL 的真实生产系统中,绝大部分的查询都非常迅速;在另外一个真实的案例中,对1000多亿条数据构建了立方体,90%的查询性能都在1.18s以内,可见 Kylin在超大规模数据集上表现优异。

2.2.4 BI可视化工具集成

Apache Kylin 提供了丰富的 API ,以与现有的 BI 工具集成,具体包括如下内容。

ODBC 接口:与 Tableau Excel Power BI 等工具集成。

JDBC 接口:与 Saiku BIRT Java 工具集成。

Rest API :与 JavaScript Web 网页集成。

2.3 Kylin工作原理

kylin 的工作原理:典型的空间换时间。利用 cube 计算的结构加速我们的查询。具体过程如下。

1.指定数据模型,定义维度和度量。

2.预计算 Cube ,计算所有 Cuboid 并保存为物化视图。

3.执行查询时,读取 Cuboid ,运算,产生查询结果。

kylin 的查询过程不会扫描原始记录,而是通过预计算预先完成表的关联、聚合等复杂运算,并利用预计算的结果来执行查询,因此相比非预计算的查询技术,其速度一般要快一到两个数量级,并且这点在 超大的数据集上优势更明显。当数据集达到千亿乃至万亿级别时, Kylin 的速度甚至可以超越其他非预计算技术1000倍以上。

总结:

kylin 核心思想是 Cube 预计算,理论基础是空间换时间,把高复杂度的聚合运算、多表连接等操作转换成对预计算结果的查询。

2.4 Kylin技术架构

Kylin系统主要分为在线查询离线构建两个部分

kylin 是一个开源的分布式分析引擎,提供一个标准的 sql 接口,给我们多维分析( OLAP ) 提供帮助。你可以把 kylin 理解为一个数据仓库。对比之前的 hive ,想要一些计算结果我们 可能会写一些脚本实现将结果集算好,但是对应业务复杂,维度变化更多的情况,你用 shell 脚本就不好控制了。 Kylin 就是按照你想要的维度给你全部构建好(即说白了你想要怎样的维度组合数据我就帮你算好)。

户可以从上方查询系统发送 SQL 进行查询分析。 Kylin 提供了各种 Rest API 、JDBC/ODBC 接口。无论从哪个接口进入, SQL 最终都会来到 Rest 服务层,再转交给查询引擎进行处理。这里需要注意的是, SQL 语句是基于数据源的关系模型书写的,而不是 Cube

kylin 在设计时刻意对查询用户屏蔽了 Cube 的概念,分析师只需要理解简单的关系模型就可以使用 Kylin ,没有额外的学习门槛,传统的 SQL 应用也很容易迁移。查询引擎解析 SQL ,生成基于关系表的逻辑执行计划,然后将其转译为基于 Cube 的物理执行计划,最后查询预计 算生成的 Cube 并产生结果。整个过程不会访问原始数据源。

注意:对于查询引擎下方的路由选择,在设计者曾考虑过将 Kylin 不能执行的查询引导去 Hive 中继续执行,但在实践后发现 Hive Kylin 的速度差异过大,导致用户无法对查询的速度有一致的 期望,很可能大多数查询几秒内就返回结果了,而有些查询则要等几分钟到几十分钟,因此体验非 常糟糕。最后这个路由功能在发行版中默认关闭,因此在图中是用虚线表示的。

总结:hive 查询时间随着数据量的增长而线性增长, kylin 使用预计算技术打破了这一点,kylin 在数据集规模上的局限性主要取决于维度的个数和基数,而不是数据集的大小,所以 Kylin 能更好地支持海量数据集的查询。而也正是预计算技术, kylin 的查询速度非常快,亚秒级响应。

 2.5 Kylin总结:

传统技术,如大规模并行计算和列式存储的查询速度都在 O(N) 级别,与数据规模增长呈线性关系。

如果数据规模增长10倍,那么 O(N) 的查询速度就会下降到十分之一,无法满足日益增长的数据需求。依靠 Apache Kylin ,我们不用再担心查询速度会随着数据量的增长而减慢。

三、Kylin的操作

3.1 操作步骤

建立project ->载入表Load Table->建立 model ->建立 cube -> build cube 。

3.2 创建Hive 表

3.3 创建project

在 kylinweb 页面创建 project

3.4 载入数据表

将hive 中事实表数据导入 kylin ;

3.5 创建模型model

3.6 创建cube

3.7 构建cube

新创建的 Cube 只有定义,而没有计算的数据,它的状态是 DISABLED ,是不会被查询引擎挑中的。

要想让 Cube 有数据,还需要对它进行构建 build 。

C的构建方式通常有两种:全量构建和增量构建;两者的构建步骤是完全一样的,区别只在于构建时读取的数据源是全集还是子集。

点击build ,对定义好的 cube 进行构建计算。构建计算完 cube 后,就可以在 Insight 页面进行查询操作。

四、Cube创建过程

1.创建临时的 Hive 平表(从 Hive 读取数据)。

2.计算各维度的不同值,并收集各 Cuboid 的统计数据。

3.创建并保存字典。

4.保存 Cuboid 统计信息。

5.创建 HTable 。

6.计算 Cube (一轮或若干轮 MapReduce )。

7.将 Cube 的计算结果转成 HFile 。

8.加载 HFile 到 HBase 。

9.更新 Cube 元数据。

10.垃圾回收。

五、kylin访问的方式

之前我们成功创建了 kylin 的 cube ,并且可以使用 web ui 查询.但除了通过 web ui 进行操作,我们还可以使用 api 调用。

5.1 RestFul API

在使用之前,我们要先进行认证,目前 Kylin 使用 basic Authentication 。 BasicAuthentication 是一种非常简单的访问控制机制,它先对账号密码基于 Base64 编码,然后将其作为请求头添加到 HTTP 请求头中,后端会读取请求头中的账号密码信息以进行认证。

以Kylin 默认的账号密码 ADMIN / KYLIN 为例,对相应的账号密码进行编码后,结果为 BasicQURNSU46S1lMSU4= ,那么 HTTP 对应的头信息 则为 Authorization:BasicQURNSU46S1lMSU4= 。

查询SQL

url -X POST -H "Authorization: Basic QURNSU46S1lMSU4=" -H 'ContentType: application/json' http://localhost:7070/kylin/api/query -d '{
"sql":"select market,sum(sales) from kylin_sale group by market",
"offset":0,
"limit":10,
"acceptPartial":false,
"project":"my_kylin"
}'

sql:必填,字符串类型,请求的 SQL 。

offset :可选,整型,查询默认从第一行返回结果,可以设置该参数以决定返回数据从哪一行开始往后返回。

limit :可选,整型,加上 limit 参数后会从 offset 开始返回对应的行数, 返回数据行数小于 limit 的将以实际行数为准。

acceptPartial :可选,布尔类型,默认是 true ,如果为 true ,那么实际上最多会返回一百万行数据;如果要返回的结果集超过了一百万行,那么 该参数需要设置为 false 。

project :可选,字符串类型,默认为 DEFAULT ,在实际使用时,如 果对应查询的项目不是 DEFAULT ,那就需要设置为自己的项目。

put提交cube

curl -X PUT -H "Authorization: Basic QURNSU46S1lMSU4=" -H 'Content-Type:
application/json' http://localhost:7070/kylin/api/cubes/my_cube/build -
d '{
"startTime":0,
"endTime":"$endTime",
"buildType":"BUILD"
}'

url 里面的 my_cube 是你自己的 cube 名, endTime 是你需要去指定的,

startTime : 当 cube 中不存在 segment 时,可以将其设置为0,那么就会从头开始算。做增量时, startTime 要设置为上一次 build 的 endTime

endTime :时间精确到毫秒(例1388563200000)。

buildType :可选 BUILD , MERGE , REFRESH 。

5.2 JAVA API

pom.xml

<!-- https://mvnrepository.com/artifact/org.apache.kylin/kylin-jdbc -->
<dependency>
<groupId>org.apache.kylin</groupId>
<artifactId>kylin-jdbc</artifactId>
<version>3.1.2</version>
</dependency>

代码实现

package com.yjxxt;
import java.io.IOException;
import java.sql.*;
public class HelloKylinJdbc {
public void getConnection() throws InstantiationException,
IllegalAccessException, ClassNotFoundException, SQLException {
Class.forName("org.apache.kylin.jdbc.Driver");
Connection connection =
DriverManager.getConnection("jdbc:kylin://192.168.88.101:7070/yjx_sales"
, "ADMIN", "KYLIN");
Statement state = connection.createStatement();
ResultSet resultSet = state.executeQuery("select
market,item,sum(sales) from kylin_sale group by market,item");
while (resultSet.next()) {
System.out.println(resultSet.getString(1) + "\t" +
resultSet.getString(2) + "\t" + resultSet.getInt(3));
}
resultSet.close();
state.close();
connection.close();
}
public static void main(String[] args) throws IOException,
InstantiationException, IllegalAccessException, ClassNotFoundException,
SQLException {
new Hello01KylinJdbc().getConnection();
}
}

六、增量cube

cube 划分为多个 Segment ,每个 Segment 用起始时间和结束时间来标志。

Segment 代表一段时间内源数据的预计算结果。

在大部分情况下一个 Segment 的起始时间等于它之前那个 Segment 的结束时间,同理,它的结束时间等于它后面那个 Segment 的起始时间。

同一个 Cube 下不同的 Segment 除了背后的源数据不同之外,其他如结构定义、构建过程、优化方法、存储方式等都完全相同。

6.1 增量OR全量

全量构建和增量构建的详细对比如下图:

对于全量构建来说,每当需要更新 Cube 数据的时候,它不会区分历史数据和新加入的数据,也就是说,在构建的时候会导入并处理所有的原始数据。

而增量构建只会导入新 Segment 指定的时间区间内的原始数据,并只对这部分原始数据进行预计算。

6.2 设计增量构建的前提

 并非所有的 Cube 都适用于增量构建, Cube 的定义必须包含一个时间维度,用来分割不同的Segment ,我们将这样的维度称为分割时间列 ( Partition Date Column )。分割时间列既可以是 Hive 中的 Date 类型、也可以是 Timestamp 类型或 String 类型。

Kylin 都要求用户显式地指定分割时间列的数据格式,例如精确到年月日的 Date 类型(或者 String 类型)的数据格式可能是 yyyyMMdd 或 yyyy-MM-dd ,如果是精确到时分秒的 Timestamp 类型(或者 String类型),那么数据格式可能是 YYYY-MM-DD HH:MM:SS`。

6.3 触发增量构建

在Web UI 上触发 Cube 的增量构建与触发全量构建的方式基本相同。在 Web UI 的 Model 页面中,选中想要增量构建的 Cube ,单击 Action → Build 。

不同于全量构建,增量构建的 Cube 会在此时弹出对话框让用户选择 End Date 。 Kylin 要求增量Segment 的起始时间等于 Cube 中最后一个 Segment 的结束时间,因此当我们为一个已经有Segment 的 Cube 触发增量构建的时候, Start Date 的值已经被确定,且不能修改。

仅当 Cube 中不存在任何 Segment ,或者不存在任何未完成的构建任务 时, Kylin 才接受该 Cube上新的构建任务。

而如果不是 web GUI 触发,你通过 restAPI 触发的话,那么要确保你给定的时间,不重合。

6.4 自动合并

在Cube Designer 的 Refresh Settings 的页面中有 Auto Merge Thresholds 和 RetentionThreshold 两个设置项可以用来帮助管理 Segment 碎片。

Auto Merge Thresholds 允许用户设置几个层级的时间阈值,层级越靠后,时间阈值就越大。举 例来说,用户可以为一个 Cube 指定(7天、28天)这样的层级。每当 Cube 中有新的 Segment 状态 变为 READY 的时候,就会触发一次系统试图自动合并的尝试。系统首先会尝试最大一级的时间阈 值,结合上面的(7天、28天)层级的例子,首先查看是否能将连续的若干个 Segment 合并成为一 个超过28天的大 Segment ,在挑选连续 Segment 的过程中,如果遇到已经有个别 Segment 的时间 长度本身已经超过了28天,那么系统会跳过该 Segment ,从它之后的所有 Segment 中挑选连续的 累积超过28天的 Segment 。如果满足条件的连续 Segment 还不能够累积超过28天,那么系统会使 用下一个层级的时间阈值重复寻找的过程。每当找到了能够满足条件的连续 Segment ,系统就会触 发一次自动合并 Segment 的构建任务,在构建任务完成之后,新的 Segment 被设置为 READY 状 态,自动合并的整套尝试又需要重新再来一遍。

举例来说,如果现在有 A~H 8个连续的 Segment ,它们的时间长度分别 为28天(A)、7天(B)、 1天(C)、1天(D)、1天(E)、1天(F)、1天(G)、1天 (H)。此时第9个 Segment I加 入,它的时间长度为1天,那么现在 Cube 中总 共存在9个 Segment 。系统首先尝试能否将连续的 Segment 合并到28天这个阈值上,由于 Segment A已经超过28天,它会被排除。

接下来的B到H加起 来也不足28天,因此第一级的时间阈值无法满足,退一步系统尝试第二级的时 间阈值,也就是7天。系统重新扫描所有的 Segment ,发现A和B已经超过7天,因此跳过它们,接 下来发现将 Segment C到I合并起来可以达到7 天的阈值,因此系统会提交一个合并 Segment 的构 建请求,将 Segment C到I 合并为一个新的 Segment X。X的构建完成之后, Cube 中只剩下三个 Segment ,分别是原来的A(28天),B(7天)和新的X(7天)。由于X的加入,触发了系统重新 开始整个合并尝试,但是发现已经没有满足自动合并的条件,既没有连续的、满足条件的、累积超 过28天的Segment,也没有连续的、满足条件的、累积超过7天的 Segment ,尝试终止。

6.5 保留Segment

从碎片管理的角度来说,自动合并是将多个 Segment 合并为一个 Segment ,以达到清理碎片的目 的。保留 Segment 则是从另外一个角度帮助实现碎片管理,那就是及时清理不再使用的 Segment 。

在很多业务场景 中,只会对过去一段时间内的数据进行查询,例如对于某个只显示过去1 年数据的 报表,支撑它的 Cube 事实上只需要保留过去一年内的 Segment 即可。由于数据在 Hive 中往往已 经存在备份,因此无需再在 Kylin 中备份超过一年的历史数据。

在这种情况下,我们可以将 Retention Threshold 设置为365。每当有新的 Segment 状态变为 READY 的时候,系统会检查每一个 Segment :如果它的结束时间距离最晚的一个 Segment 的结束 时间已经大于 Retention Threshold ,那么这个 Segment 将被视为无需保留。系统会自动地从 Cube 中 删除这个 Segment 。

如果启用了 Auto Merge Thresholds ,那么在使用 Retention Threshold 的时候需要注意,不 能将 Auto Merge Thresholds 的最大层级设置得太高。假设我们将 Auto Merge Thresholds 的 最大一级设置为1000天,而将 Retention Threshold 设置为365天,那么受到自动合并的影响, 新加入的Segment会不断地被自动合并到一个越来越大的 Segment 之中,但是这样会导致 kylin 要去不断地更新这个大 Segment 的结束时间,从而导致这个大 Segment 永远不会得到释放。因 此,推荐自动合并的最大一级的时间不要超过1年。

6.6 数据持续更新

在实际应用场景中,我们常常会遇到这样的问题:由于 ETL 过程的延迟,业务每天都需要刷新过去 N天的 Cube 数据。

举例来说,客户有一个报表每天都需要更新,但是每天的源数据更新不仅包含了当天的新数据, 还 包括了过去7天内数据的补充。

一种比较简单的方法是,每天在 Cube 中增量构建一个长度为一天的 Segment ,这样过去7天的数 据就会以7个 Segment 的形式存在于 Cube 之中。 Cube 的管理员除了每天要创建一个新的 Segment 代表当天的新数据( BUILD 操作)以外,还需要对代表过去7天的7个 Segment 进行刷新 ( REFRESH 操作, Web UI 上的操作及 Rest API 参数与 BUILD 类似,这里不再详细展开)。这样 的方法固然可以奏效,但是每天为每个 Cube 触发的构建数量太多,容易造成 Kylin 的任务队列堆 积大量未能完成的任务。

上述简单方案的另外一个弊端是,每天一个 Segment 也会让 Cube 中迅速地累积大量的 Segment , 需要Cube管理员手动地对历史已经超过7天的 Segment 进行合并,期间还必须小心地操作,不能 错将7天内的 Segment 一 起合并了。

举例来说,假设现在有100个 Segment ,每个 Segment 代表过去的一天的数据, Segment 按照起 始时间排序。在合并时,我们只能挑选前面 93个 Segment 进行合并,如果不小心把第94个 Segment 也一起合并了,那么 当我们试图刷新过去7天(94~100)的 Segment 的时候,会发现为 了刷新第94天的数据,不得不将1~93的数据一并重新计算,这对于刷新来说是一种极大的浪费。 而且最差的情况就是即使使用之前所介绍的自动合并的功能,类似的问题也仍然存在。

解决思路:

不以日为单位创建新的 Segment , 而是以N天为单位创建新的 Segment 。

举例来说,假设用户每天更新 Cube 的时候,前面7天的数据都需要更新一下,也就是说,如 果今天是01-08, 那么用户不仅要添加01-08的新数据,还要同时更新01-01到01-07的数据。 在这种情况下,可设置N=7作为最小 Segment 的长度。在第一天01-01, 创建一个新的 Segment A,它的时间是从01-01到01-08,我们知道 Segment 是起始时间闭,结束时间开, 因此 Segment A的真实长度为7天,也就是01-01到01-07。即使在01-01当天,还没有后面几 天的数据, Segment A也能正 常地构建,只不过构建出来的 Segment 其实只有01-01一天的数据而已。从 01-02到01-07的每一天,我们都要刷新 Segment A,以保证1日到7日的数据保 持更新。由于01-01已经是最早的日期,所以不需要对更早的数据进行更新。

到01-08的时候,创建一个新的 Segment B,它的时间是从01-08到01-15。此时我们不仅需要 构建 Segment B,还需要去刷新 Segment A。因为01-01到01-07中的数据在01-08当天仍然 可能处于更新状态。在接下来的01-09到01-14,每天刷新A、B两个 Segment 。等到了01-15 这天的时候,首先创建一个新的 Segment C,它的时间是从01-15到01-22。在01-15当天, Segment A的数据应当已经被视作最终状态,因为 Segment A中的最后一天 (01-07)已经 不再过去N天的范围之内了。因此此时接下来只需要照顾 Segment B和 Segment C即可。

由此可以看到,在任意一天内,我们只需要同时照顾两个 Segment , 第一个 Segment 主要以 刷新近期数据为主,第二个 Segment 则兼顾了加入新数据与刷新近期数据。这个过程中可能 存在少量的多余计算,但是每天多余计算的数据量不会超过N天的数据量。这对于 Kylin 整体 的计算量来说是可以接受的。根据业务场景的不同,N可能是7天,也有可能是30天,我们可 以适度地把最小的 Segment 设置成比N稍微大一点的数字。

例如N为7的时候,我们可以设置为10天,这样即使 ETL 有时候没有能够遵守N=7的约定,也 仍然能够刷新足够的数据。

七、Kylin优化

按照 dimension 大小顺序排序,从 Base Cuboid 开始,依次基于上一层 Cuboid 的结果进行再聚 合。每一层的计算都是一个单独的 Map Reduce(Spark) 任务。

一个N维的 Cube ,便有2的N次方种维度组合,参考 kylin 官网上的一个例子,一个 Cube 包含 time, item, location, supplier 四个维度,那么组合( Cuboid )便有16种。

一个 Cube 中,当维度数量N超过一定数量后,空间以及计算消耗将会非常大,比如说10维那就是 1024个 cuboid ,但我们真正查询的时候可能只会用到其中的100个cuboid,如果不做优化那么会 出现以下几个问题.

会使得 Build 出来的 Cube Size 很大,从而占用大量的磁盘空间; Cube Building 的时间会很长 ; 会占用集群的计算资源 。

7.1 Cube 剪枝优化

7.1.1 聚合组 Aggravation Group

Kylin 在定义 Cube 时候,可以将维度拆分成多个聚合组( Aggregation Groups )这也就是我们在 web 页面创建 cube 时的第5步可以做,只在组内计算 Cube ,聚合组内查询效率高,跨组查询效率较差,所 以需要根据业务场景,将常用的维度组合定义到一个聚合组中,提高查询性能,这也是 Kylin 中查询性 能优化的一个重要方面。

举例:

业务场景有4个维度,分别为ABCD,如果聚合组含有的维度为ABCD的话,它的Cuboid为2^4=16个。但 是此时如果AB是一个聚合组,CD是一个聚合组,那么Cuboid的个数就是2^2+2^2=8个,相当于缩减了 一半。即原来2^(K+M+N)个Cuboid可以减少到2^K+2^M+2^N个。

查看cube所生成的cuboid个数命令如下:

kylin.sh org.apache.kylin.engine.mr.common.CubeStatsReader CubeName

7.1.1.1 强制维度( Mandatory Dimensions )

如果一个维度被定义为强制维度,那么这个分组产生的所有 Cuboid 中每一个 Cuboid 都会包含该维度。 如果根据这个分组的业务逻辑,相关的查询一定会在过滤条件或分组条件中,则可以在该分组中把该维 度设置为强制维度。

7.1.1.2层次维度( Hierarchy Dimensions )

何为层次维度,具有上下级层次关系的维度,例如时间维度 年—>月—>日 ,和地区维度 国家(country)— >省份(province)—> 城市(city) 。将有这样关系的维度设置为层次关系,并且将它们设置为层次维 度的话, cuboid 数量将下降。

举例:

如果有三个维度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 。

Hive 里面的数据以时间作为分区,每天处理增量数据,那么我们 kylin 也要每天增量写入数据。 

8.1.1.3 联合维度(Joint Dimensions)

每个联合中包含两个或更多个维度,如果某些列形成一个联合,那么在该分组产生的任何 Cuboid 中, 这些联合维度要么一 起出现,要么都不出现。如果根据这个分组的业务逻辑,多个维度在查询中总是同 时出现,则可以在该分组中把这些维度设置为联合维度。

例子:如有 A B C D 四个维度, B C维度作为联合维度,则最后只要生成7个维度,如下图所示:其中红 色维度为无需生成的维度,黑色维度为需要生成的维度

7.1.2 衍生维度

时间维度表,里面充斥着用途各异的时间维度,例如每个日期所处的季度、月份等。这些维度可以被地 用来进行各个时间粒度上的聚合分析,而不需要进行额外的上卷( Roll Up )操作。但是为了这个目的 一下子引入这么多个维度,导致 Cube 中总共的 Cuboid 数量呈现爆炸式的增长往往是得不偿失的。

当用户需要以更高的粒度(比如按周、 按月)来聚合时,如果在查询时获取按日聚合的 Cuboid 数据, 并在查询引擎中实时地进行上卷操作,那么就达到了使用牺牲一部分运行时性能来节省 Cube 空间占用 的目的。

Kylin 将这样的理念包装成为了一个简单的优化工具——衍生维度。

衍生维度用于在有效维度内将维度表上的非主键维度排除掉,并使用维度表的主键(其实是事实表上相 应的外键)来替代它们。 Kylin 会在底层记录维度表主键与维度表其他维度之间的映射关系,以便在查 询时能够动 态地将维度表的主键翻译成这些非主键维度,并进行实时聚合。

例如,假设有一个日期查找表,其中的 cal_dt 是主键列,还有许多派生列,例如 week_begin_dt , mont9h_begin_dt 。即使分析人员需要 week_begin_dt 作为维度,我们也可以对其进行修剪,因为它 始终可以从 cal_dt 维度进行计算,这是“派生”优化。

7.2 Rowkey编码优化

编码( Encoding )代表了该维度的值应使用何种方式进行编码,合适的编码能够减少维度对空间的 占用,例如,我们可以把所有的日期都用三个字节进行编码,相比于字符串存储,或者是使用长整数形 式存储的方法,我们的编码方式能够大大减少每行 Cube 数据的体积。而 Cube 中可能存在数以亿计的行 数,使用编码节约的空间累加起来将是一个非常巨大的数字。

目前 Kylin 支持的编码方式有以下几种:

Date 编码:将日期类型的数据使用三个字节进行编码,其支持从 000-01-01 到 9999-01-01 中的每 一个日期。

Time 编码:仅支持表示从 1970-01-01 00:00:00 到 2038-01-19 03:14:07 的时间,且 Timestamp 类型的维度经过编码和反编码之后,会失去毫秒信息,所以说 Time 编码仅仅支持到秒。但 是 Time 编码的优势是每个维度仅仅使用4个字节,这相比普通的长整数编码节约了一半。如果能够 接受秒级的时间精度,请选择 Time 编码来代表时间的维度。

Integer 编码: Integer 编码需要提供一个额外的参数 Length 来代表需要多少个字节。 Length 的长度为1~8。如果用来编码 int32 类型的整数,可以将 Length 设为4;如果用来编码 int64 类 型的整数,可以将 Length 设为8。在更多情况下,如果知道一个整数类型维度的可能值都很小,那 么就能使用 Length 为2甚至是1的 int 编码来存储,这将能够有效避免存储空间的浪费。

Dict 编码:对于使用该种编码的维度,每个 Segment 在构建的时候都会为这个维度所有可能的值 创建一个字典,然后使用字典中每个值的编 号来编码。 Dict 的优势是产生的编码非常紧凑,尤其 在维度值的基数较小且长度较大的情况下,特别节约空间。由于产生的字典是在查询时加载入构建 引擎和查询引擎的,所以在维度的基数大、长度也大的情况下,容易造成构建引擎或查询引擎的内 存溢出。

Fixed_length 编码:编码需要提供一个额外的参数 Length 来代表需 要多少个字节。该编码可以 看作 Dict 编码的一种补充。对于基数大、长度也大的维度来说,使用 Dict 可能不能正常工作,于 是可以采用一段固定长度的字节来存储代表维度值的字节数组,该数组为字符串形式的维度值的 UTF-8 字节。如果维度值的长度大于预设的 Length ,那么超出的部分将会被截断。

Fixed_Length_Hex 编码:适用于字段值为十六进制字符,比如 1A2BFF 或者 FF00FF ,每两个字符 需要一个字节。只适用于 varchar 或 nvarchar 类型.

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值