【硬刚Kylin】Kylin入门/原理/调优/OLAP解决方案和行业典型应用

2021-07-12 16:03:01 浏览数 (1)

一:背景历史和使命

背景和历史

现今,大数据行业发展得如火如荼,新技术层出不穷,整个生态欣欣向荣。作为大数据领域最重要的技术的 Apache Hadoop 最初致力于简单的分布式存储,然后在此基础之上实现大规模并行计算,到如今在实时分析、多维分析、交互式分析、机器学习甚至人工智能等方面有了长足的发展。

2013 年年初,在 eBay 内部使用的传统数据仓库及商业智能平台碰到了“瓶颈”。年中,eBay 公司启动了一个大数据项目,其中有一部分内容就是 BI on Hadoop 的预研。经过一年多的研发,2014 年 9 月底,代号 Kylin 的大数据平台在 eBay 内部正式上线。10 月 1 日,Kylin 项目负责人韩卿将 Kylin 的源代码提交到 github.com 并正式开源。当天就得到了业界专家的关注和认可。

图片所示为 Hortonworks 的 CTO 对 Apache Kylin 的 Twitter 评价。

Hadoop 社区的许多朋友都鼓励 eBay 将该项目贡献到 Apache 软件基金会(ASF),让它与其他大数据项目一起获得更好的发展。Kylin 项目于 2014 年 11 月正式加入 Apache 孵化器项目。2015 年 11 月,Apache 软件基金会宣布 Apache Kylin 正式成为顶级项目。这是第一个完全由中国团队贡献到全球最大的开源软件基金会的顶级项目。

2016 年 3 月,由 Apache Kylin 核心开发者组建的创业公司 Kyligence 正式成立。

2015 年 9 月,Apache Kylin 与 Apache Spark、Apache Kafka、H2O、Apache Zeppelin 等一同获得了 2015 年度“最佳开源大数据工具奖”。这是业界对 Apache Kylin 的充分认可和褒奖。2016 年的 InfoWorld 获奖榜单进一步收窄,获奖者数量较前一年减少一半,值得骄傲的是,Apache Kylin 再次登上领奖台,蝉联“最佳开源大数据工具奖”。

Apache Kylin 在社区开发者的共同努力下进一步发展和完善,先后发布了 1.6、2.0~ 2.5 多个版本,涵盖近实时流、Spark 引擎、RDBMS 数据源、Cube Planner,支持 Hadoop 3.0 等众多新功能,还有一些新功能正在进行公开 beta 测试,如 Parquet 存储引擎、完全实时流数据等,预计在不远的将来会正式发布。同时,Apache Kylin 用户群也在不断发展壮大,跨越亚洲、美洲、欧洲、澳洲等地。据粗略计算,全球已经有超过一千家企业将 Apache Kylin 用于自身的关键业务分析。

Apache Kylin 的使命

Apache Kylin 的使命是实现超高速的大数据 OLAP 分析,也就是要让大数据分析像使用数据库一样简单迅速,用户的查询请求可以在秒级返回,交互式数据分析以前所未有的速度释放大数据里潜藏的知识和信息,以使我们在面对未来的挑战时占得先机。

二:工作原理

工作原理本质上是 MOLAP(Multidimensional Online Analytical Processing) Cube,也就是多维立方体分析。下面对其做简要介绍。

维度和度量简介

在说明 MOLAP Cube 之前,需要先介绍一下维度(dimension)和度量(measure)这两个概念。

维度就是观察数据的角度,比如电商的销售数据,可以从时间的维度来观察(如图 1 的左图所示),也可以进一步细化从时间和地区的维度来观察(如图 1 的右图所示)。维度一般是一组离散的值,比如时间维度上的每一个独立的日期,或者商品维度上的每一件独立的商品。因此,统计时可以把维度值相同的记录聚合起来,应用聚合函数做累加、平均、去重复计数等聚合计算。

图 1 维度和度量

度量就是被聚合的统计值,也是聚合运算的结果,它一般是连续值,如图 1 中的销售额,抑或是销售商品的总件数。通过比较和测算度量,分析师可以对数据进行评估,比如今年的销售额相比去年有多大的增长、增长的速度是否达到预期、不同商品类别的增长比例是否合理等。

Cube 和 Cuboid

了解了维度和度量,就可以对数据表或者数据模型上的所有字段进行分类了,它们要么是维度,要么是度量(可以被聚合)。于是就有了根据维度、度量做预计算的 Cube 理论。

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

计算 Cuboid,就是按维度来聚合销售额(GMV)。如果用 SQL 来表达计算 Cuboid [Time, Location],那就是:

代码语言:javascript复制
select Time, Location, Sum(GMV) as GMV from Sales group by Time, Location

图 2 四维 Cube

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

工作原理

Apache Kylin 的工作原理就是对数据模型做 Cube 预计算,并利用计算的结果加速查询。过程如下:

1.指定数据模型,定义维度和度量。 2.预计算 Cube,计算所有 Cuboid 并将其保存为物化视图。 3.执行查询时,读取 Cuboid,进行加工运算产生查询结果。

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

三:技术架构

Apache Kylin 系统可以分为在线查询和离线构建两部分,其技术架构如图 1 所示。在线查询主要由上半区组成,离线构建在下半区。

先看离线构建的部分。从图 1 中可以看到,数据源在左侧,目前主要是 Hadoop、Hive、Kafka 和 RDBMS,其中保存着待分析的用户数据。根据元数据定义,下方构建引擎从数据源中抽取数据,并构建 Cube。数据以关系表的形式输入,且必须符合星形模型(Star Schema)或雪花模型(Snowflake Schema)。用户可以选择使用 MapReduce 或 Spark 进行构建。构建后的 Cube 保存在右侧的存储引擎中,目前 HBase 是默认的存储引擎。

完成离线构建后,用户可以从上方查询系统发送 SQL 来进行查询分析。Kylin 提供了多样的 REST API、JDBC/ODBC 接口。无论从哪个接口进入,最终 SQL 都会来到 REST 服务层,再转交给查询引擎进行处理。这里需要注意的是,SQL 语句是基于数据源的关系模型书写的,而不是 Cube。Kylin 在设计时刻意对查询用户屏蔽了 Cube 的概念,分析师只需要理解简单的关系模型就可以使用 Kylin,没有额外的学习门槛,传统的 SQL 应用也更容易迁移。查询引擎解析 SQL,生成基于关系表的逻辑执行计划,然后将其转译为基于 Cube 的物理执行计划,最后查询预计算生成的 Cube 产生结果。整个过程不访问原始数据源。

注意

对于查询引擎下方的路由选择,在最初设计时考虑过将 Kylin 不能执行的查询引导到 Hive 中继续执行。但在实践后发现 Hive 与 Kylin 的执行速度差异过大,导致用户无法对查询的速度有一致的期望,大多语句很可能查询几秒就返回了,而有些要等几分钟到几十分钟,用户体验非常糟糕。最后这个路由功能在发行版中默认被关闭。

Apache Kylin v1.5 版本引入了“可扩展架构”的概念。图 1 所示为 Rest Server、Cube Build Engine 和数据源表示的抽象层。可扩展是指 Kylin 可以对其三个主要依赖模块—数据源、构建引擎和存储引擎,做任意的扩展和替换。在设计之初,作为 Hadoop 家族的一员,这三者分别是 Hive、MapReduce 和 HBase。但随着 Apache Kylin 的推广和使用的深入,用户发现它们存在不足之处。

这也为 Kylin 技术的与时俱进奠定了基础。如果将来有更先进的分布式计算技术可以取代 MapReduce,或者有更高效的存储系统全面超越了 HBase,Kylin 可以用较小的代价将一个子系统替换掉,从而保证 Kylin 紧跟技术发展的最新潮流,保持最高的技术水平。

可扩展架构也带来了额外的灵活性,比如,它可以允许多个引擎并存。例如,Kylin 可以同时对接 Hive、Kafka 和其他第三方数据源;抑或用户可以为不同的 Cube 指定不同的构建引擎或存储引擎,以期达到极致的性能和功能定制。

Apache Kylin 的主要特点

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

四:核心概念

数据仓库、OLAP 与 BI

数据仓库(Data Warehouse)是一种信息系统的资料储存理论,此理论强调的是利用某些特殊资料储存方式,让所包含的资料特别有利于分析处理,从而产生有价值的资讯并依此做决策。

OLAP(Online Analytical Process),即联机分析处理,它可以以多维度的方式分析数据,并且能弹性地提供上卷(Roll-up)、下钻(Drill-down)和透视分析(Pivot)等操作,是呈现集成性决策信息的方法,其主要功能在于方便大规模数据分析及统计计算,多用于决策支持系统、商务智能或数据仓库。与之相区别的是联机交易处理(OLTP),联机交易处理侧重于基本的、日常的事务处理,包括数据的增、删、改、查。

  • OLAP 需要以大量历史数据为基础,配合时间点的差异并对多维度及汇整型的信息进行复杂的分析。
  • OLAP 需要用户有主观的信息需求定义,因此系统效率较高。

BI(Business Intelligence),即商务智能,是指用现代数据仓库技术、在线分析技术、数据挖掘和数据展现技术进行数据分析以实现商业价值。

维度建模

维度建模用于决策制定,并侧重于业务如何表示和理解数据。基本的维度模型由维度和度量两类对象组成。维度建模尝试以逻辑、可理解的方式呈现数据,以使得数据的访问更加直观。维度设计的重点是简化数据和加快查询。

维度模型是数据仓库的核心。它经过精心设计和优化,可以为数据分析和商业智能(BI),检索并汇总大量的相关数据。在数据仓库中,数据修改仅定期发生,并且是一次性开销,而读取是经常发生的。对于一个数据检索效率比数据处理效率重要得多的数据结构而言,非标准化的维度模型是一个不错的解决方案。

在数据挖掘中有几种常见的多维数据模型,如星形模型(Star Schema)、雪花模型(Snowflake Schema)、事实星座模型(Fact Constellation)等。

事实表和维度表

事实表(Fact Table)是指存储事实记录的表,如系统日志、销售记录等,并且是维度模型中的主表,代表着键和度量的集合。事实表的记录会不断地动态增长,所以它的体积通常远大于其他表,通常事实表占据数据仓库中 90%或更多的空间。

维度表(Dimension Table),也称维表或查找表(Lookup Table),是与事实表相对应的一种表。维度表的目的是将业务含义和上下文添加到数据仓库中的事实表和度量中。维度表是事实表的入口点,维度表实现了数据仓库的业务接口。它们基本上是事实表中的键引用的查找表。它保存了维度的属性值,可以与事实表做关联,相当于将事实表上经常出现的属性抽取、规范出来用一张表进行管理,常见的维度表有:日期表(存储日期对应的 周、月、季度等属性)、地点表(包含国家、省/州、城市等属性)等。使用维度表的好处如下:

  • 减小了事实表的大小;
  • 便于维度的管理和维护,增加、删除和修改维度的属性时,不必对事实表的大量记录进行改动;
  • 维度表可以为多个事实表同时使用,减少重复工作。

维度和度量

维度是人们观察数据的特定角度,是考虑问题时的一类属性。它通常是数据记录的一个特征,如时间、地点等。同时,维度具有层级概念,可能存在细节程度不同的描述方面,如日期、月份、季度、年等。

可以在数学上求和的事实属性称为度量。例如,可以对度量进行总计、平均、以百分比形式使用等。度量是维度模型的核心。通常,在单个查询中检索数千个或数百万个事实行,其中对结果集执行数学方程。

Cube、Cuboid 和 Cube Segment

Cube(或称 Data Cube),即数据立方体,是一种常用于数据分析与索引的技术,它可以对原始数据建立多维度索引,大大加快数据的查询效率。

Cuboid 特指 Apache Kylin 中在某一种维度组合下所计算的数据。

Cube Segment 指针对源数据中的某一片段计算出来的 Cube 数据。通常,数据仓库中的数据数量会随时间的增长而增长,而 Cube Segment 也是按时间顺序构建的。

五:快速开始

这里是一份从下载安装到体验亚秒级查询的完整流程,分别介绍了有hadoop环境(基于hadoop环境的安装)和没有hadoop环境(从docker镜像安装)两种场景下kylin的安装使用,用户可以根据自己的环境选择其中的任意一种方式。

你可以按照文章里的步骤对kylin进行初步的了解和体验,掌握kylin的基本使用技能,然后结合自己的业务场景使用kylin来设计模型,加速查询。

一、 从docker镜像安装使用kylin(不需要提前准备hadoop环境)

为了让用户方便的试用 Kylin,我们提供了 Kylin 的 docker 镜像。该镜像中,Kylin 依赖的各个服务均已正确的安装及部署,包括:

  • JDK 1.8
  • Hadoop 2.7.0
  • Hive 1.2.1
  • Hbase 1.1.2 (with Zookeeper)
  • Spark 2.3.1
  • Kafka 1.1.1
  • MySQL 5.1.73

我们已将面向用户的 Kylin 镜像上传至 docker 仓库,用户无需在本地构建镜像,只需要安装docker,就可以体验kylin的一键安装。

step1.首先执行以下命令从 docker 仓库 pull 镜像

代码语言:javascript复制
docker pull apachekylin/apache-kylin-standalone:3.1.0

此处的镜像包含的是kylin最新Release版本kylin 3.1.0。由于该镜像中包含了所有kylin依赖的大数据组件,所以拉取镜像需要的时间较长,请耐心等待。Pull成功后显示如下:

step2.执行以下命令来启动容器

代码语言:javascript复制
docker run -d 
-m 8G 
-p 7070:7070 
-p 8088:8088 
-p 50070:50070 
-p 8032:8032 
-p 8042:8042 
-p 16010:16010 
apachekylin/apache-kylin-standalone:3.1.0

容器会很快启动,由于容器内指定端口已经映射到本机端口,可以直接在本机浏览器中打开各个服务的页面,如:

  • Kylin 页面:http://127.0.0.1:7070/kylin/
  • Hdfs NameNode 页面:http://127.0.0.1:50070
  • Yarn ResourceManager 页面:http://127.0.0.1:8088
  • HBase 页面:http://127.0.0.1:60010

容器启动时,会自动启动以下服务:

  • NameNode, DataNode
  • ResourceManager, NodeManager
  • HBase
  • Kafka
  • Kylin

并自动运行 $KYLIN_HOME/bin/sample.sh及在 Kafka 中创建 kylin_streaming_topic topic 并持续向该 topic 中发送数据。这是为了让用户启动容器后,就能体验以批和流的方式的方式构建 Cube 并进行查询。用户可以通过docker exec命令进入容器,容器内相关环境变量如下:

代码语言:javascript复制
JAVA_HOME=/home/admin/jdk1.8.0_141
HADOOP_HOME=/home/admin/hadoop-2.7.0
KAFKA_HOME=/home/admin/kafka_2.11-1.1.1
SPARK_HOME=/home/admin/spark-2.3.1-bin-hadoop2.6
HBASE_HOME=/home/admin/hbase-1.1.2
HIVE_HOME=/home/admin/apache-hive-1.2.1-bin
KYLIN_HOME=/home/admin/apache-kylin-3.1.0-bin-hbase1x

使用ADMIN/KYLIN的用户名和密码组合登陆Kylin后,用户可以使用sample cube来体验cube的构建和查询,也可以按照下面“基于hadoop环境安装使用kylin”中从step8之后的教程来创建并查询属于自己的model和cube。

二、 基于hadoop环境安装使用kylin

对于已经有稳定hadoop环境的用户,可以下载kylin的二进制包将其部署安装在自己的hadoop集群。安装之前请根据以下要求进行环境检查:

  • 前置条件:

Kylin 依赖于 Hadoop 集群处理大量的数据集。您需要准备一个配置好 HDFS,YARN,MapReduce,Hive, HBase,Zookeeper 和其他服务的 Hadoop 集群供 Kylin 运行。 Kylin 可以在 Hadoop 集群的任意节点上启动。方便起见,您可以在 master 节点上运行 Kylin。但为了更好的稳定性,我们建议您将 Kylin 部署在一个干净的 Hadoop client 节点上,该节点上 Hive,HBase,HDFS 等命令行已安装好且 client 配置(如 core-site.xml,hive-site.xml,hbase-site.xml及其他)也已经合理的配置且其可以自动和其它节点同步。 运行 Kylin 的 Linux 账户要有访问 Hadoop 集群的权限,包括创建/写入 HDFS 文件夹,Hive 表, HBase 表和提交 MapReduce 任务的权限。

  • 硬件要求:

运行 Kylin 的服务器建议最低配置为 4 core CPU,16 GB 内存和 100 GB 磁盘。

  • 操作系统要求:

CentOS 6.5 或Ubuntu 16.0.4

  • 软件要求:

Hadoop 2.7 ,3.0 Hive 0.13 ,1.2.1 Hbase 1.1 ,2.0(从kylin 2.5开始支持) JDK: 1.8

建议使用集成的Hadoop环境进行kylin的安装与测试,比如Hortonworks HDP 或Cloudera CDH ,kylin发布前在 Hortonworks HDP 2.2-2.6 and 3.0, Cloudera CDH 5.7-5.11 and 6.0, AWS EMR 5.7-5.10, Azure HDInsight 3.5-3.6上测试通过。

当你的环境满足上述前置条件时 ,你可以开始安装使用kylin。

step1.下载kylin压缩包

代码语言:javascript复制
cd /usr/local/
wget http://apache.website-solution.net/kylin/apache-kylin-3.1.0/apache-kylin-3.1.0-bin-cdh57.tar.gz

step2.解压kylin

代码语言:javascript复制
tar -zxvf  apache-kylin-3.1.0-bin-cdh57.tar.gz
cd apache-kylin-3.1.0-bin-cdh57
export KYLIN_HOME=`pwd`

step3.下载SPARK

代码语言:javascript复制
export SPARK_HOME=/path/to/spark

如果没有已经下载好的Spark环境,也可以使用kylin自带脚本下载spark:

代码语言:javascript复制
$KYLIN_HOME/bin/download-spark.sh

step4.环境检查

代码语言:javascript复制
$KYLIN_HOME/bin/check-env.sh

step5.启动kylin

启动kylin:

代码语言:javascript复制
$KYLIN_HOME/bin/kylin.sh start

如果启动成功,命令行的末尾会输出如下内容:

代码语言:javascript复制
A new Kylin instance is started by root. To stop it, run 'kylin.sh stop'
Check the log at /usr/local/apache-kylin-3.1.0-bin-cdh57/logs/kylin.log
Web UI is at http://<hostname>:7070/kylin

step6.访问kylin

Kylin 启动后可以进行访问。其中 为具体的机器名、IP 地址或域名,port为kylin端口,默认为7070。初始用户名和密码是 ADMIN/KYLIN。服务器启动后,可以通过查看 $KYLIN_HOME/logs/kylin.log 获得运行时日志。

step7.创建Sample Cube

Kylin提供了一个创建样例Cube的脚本,以供用户快速体验Kylin。在命令行运行:

代码语言:javascript复制
$KYLIN_HOME/bin/sample.sh

完成后登陆kylin,点击System->Configuration->Reload Metadata来重载元数据 元数据重载完成后你可以在左上角的Project中看到一个名为learn_kylin的项目,它包含kylin_sales_cube和kylin_streaming_cube, 它们分别为batch cube和streaming cube,你可以直接对kylin_sales_cube进行构建,构建完成后就可以查询。 对于kylin_streaming_cube,需要设置KAFKA_HOME指向你的kafka安装目录:

代码语言:javascript复制
export KAFKA_HOME=/path/to/kafka

然后执行

代码语言:javascript复制
${KYLIN_HOME}/bin/sample-streaming.sh

step8.创建project

登陆kylin后,点击左上角的 号来创建Project:

step9.加载Hive表

点击Model->Data Source->Load Table From Tree,Kylin会读取到Hive数据源中的表并以树状方式显示出来,你可以选择自己要使用的表,然后点击sync进行将其加载到kylin。

step10.创建模型

点击Model->New->New Model:

输入Model Name点击Next进行下一步,选择Fact Table和Lookup Table,添加Lookup Table时需要设置与事实表的JOIN条件。

然后点击Next到下一步添加Dimension:

点击Next下一步添加Measure:

点击Next下一步跳转到设置时间分区列和过滤条件页面,时间分区列用于增量构建时选择时间范围,如果不设置时间分区列则代表该model下的cube都是全量构建。过滤条件会在打平表时用于where条件。

最后点击Save保存模型。

step11.创建Cube

选择Model->New->New Cube

点击Next到下一步添加Dimension,Lookup Table的维度可以设置为Normal(普通维度)或者Derived(衍生维度)两种类型,默认设置为衍生维度,衍生维度代表该列可以从所属维度表的主键中衍生出来,所以实际上只有主键列会被Cube加入计算。

点击Next到下一步,点击 Measure来添加需要预计算的度量。Kylin会默认创建一个Count(1)的度量。Kylin支持SUM、MIN、MAX、COUNT、COUNT_DISTINCT、TOP_N、EXTENDED_COLUMN、PERCENTILE八种度量。请为COUNT_DISTINCT和TOP_N选择合适的返回类型,这关系到Cube的大小。添加完成之后点击ok,该Measure将会显示在Measures列表中

添加完所有Measure后点击Next进行下一步,这一页是关于Cube数据刷新的设置。在这里可以设施自动合并的阈值(Auto Merge Thresholds)、数据保留的最短时间(Retention Threshold)以及第一个Segment的起点时间。

点击Next跳转到下一页高级设置。在这里可以设置聚合组、RowKeys、Mandatory Cuboids、Cube Engine等。

对于高级设置不是很熟悉时可以先保持默认设置,点击Next跳转到Kylin Properties页面,你可以在这里重写cube级别的kylin配置项,定义覆盖的属性。

配置完成后,点击Next按钮到下一页,这里可以预览你正在创建的Cube的基本信息,并且可以返回之前的步骤进行修改。如果没有需要修改的部分,就可以点击Save按钮完成Cube创建。之后,这个Cube将会出现在你的Cube列表中。

step12.构建Cube

上一个步骤创建好的Cube只有定义,而没有计算好的数据,它的状态是‘DISABLED’,是不可以查询的。要想让Cube有数据,还需要对它进行构建。

Cube的构建方式通常有两种:全量构建和增量构建。

点击要构建的Cube的Actions列下的Action展开,选择Build,如果Cube所属Model中没有设置时间分区列,则默认全量构建,点击Submit直接提交构建任务。

如果设置了时间分区列,则会出现如下页面,在这里你要选择构建数据的起止时间:

设置好起止时间后,点击Submit提交构建任务。然后你可以在Monitor页面观察构建任务的状态。Kylin会在页面上显示每一个步骤的运行状态、输出日志以及MapReduce任务。可以在${KYLIN_HOME}/logs/kylin.log中查看更详细的日志信息。

任务构建完成后,Cube状态会变成READY,并且可以看到Segment的信息。

step13.查询Cube

Cube构建完成后,在Insight页面的Tables列表下面可以看到构建完成的Cube的table,并可以对其进行查询.查询语句击中Cube后会返回存储在Hbase中的预计算结果。

调优和原理进阶

优化实战(一):资源调整

首先,就是资源方面,实际上对于Kylin的计算来说,要求的资源不算多,因为底层(默认)计算引擎是M-S-R范式基于磁盘的计算框架MapReduce,尤其是CPU,要求很低很低,但是内存配置高点的话, 还是对性能提升有好处的,一方面是计算,缓冲区大小变大,对计算帮助很大;另外一反面就是查询,因为Kylin中间结果的存储是依托于HBase的,HBase本身就很吃内存。

MapReduce方面,可以通过在Kylin的配置文件中覆盖掉原有MapReduce的参数配置。在conf目录下,kylin_job_conf.xml 和 kylin_job_conf_inmem.xml 中参数,以键值对的性质,按照如下格式替换:

代码语言:javascript复制
kylin.engine.mr.config-override.<key> = <value>

从 Yarn上 获得更多内存,可以这样设置:

代码语言:javascript复制
kylin.engine.mr.config-override.mapreduce.map.java.opts=-Xmx7g 和 kylin.engine.mr.config-override.mapreduce.map.memory.mb=10240

指定MR任务的资源队列,可以设置(在此之前,需要现在yarn上配置资源队列):

代码语言:javascript复制
kylin.engine.mr.config-override.mapreduce.job.queuename={queueName}

配置MR任务执行时的一些内存参数:

代码语言:javascript复制
mapreduce.reduce.shuffle.merge.percent(default0.66)溢写到磁盘
mapreduce.task.io.sort.mb(default:100)根据不同的硬件尤其是内存的大小来调整,调大的话,会减少磁盘spill的次数此时如果内存足够的话,一般都会显
著提升性能

这个配置可以在项目或者cube级别。HBase方面,根据自己集群配置的硬件条件,尽量给大内存。在cube构建过程中,对HBase的读写会非常频繁,合理的分配内存至关重要。

代码语言:javascript复制
HBase 存储
kylin.storage.hbase.table-name-prefix:默认值为 KYLIN_
kylin.storage.hbase.namespace:指定 HBase 存储默认的 namespace,默认值为 default
kylin.storage.hbase.coprocessor-local-jar:指向 HBase 协处理器有关 jar 包
kylin.storage.hbase.coprocessor-mem-gb:设置 HBase 协处理器内存大小,默认值为 3.0(GB)
kylin.storage.hbase.run-local-coprocessor:是否运行本地 HBase 协处理器,默认值为 FALSE
kylin.storage.hbase.coprocessor-timeout-seconds:设置超时时间,默认值为 0
kylin.storage.hbase.region-cut-gb:单个 Region 的大小,默认值为 5.0
kylin.storage.hbase.min-region-count:指定最小 Region 个数,默认值为 1
kylin.storage.hbase.max-region-count:指定最大 Region 个数,默认值为 500
kylin.storage.hbase.hfile-size-gb:指定 HFile 大小,默认值为 2.0(GB)
kylin.storage.hbase.max-scan-result-bytes:指定扫描返回结果的最大值,默认值为 5242880(byte),即 5(MB)
kylin.storage.hbase.compression-codec:是否压缩,默认值为 none,即不开启压缩
kylin.storage.hbase.rowkey-encoding:指定 Rowkey 的编码方式,默认值为 FAST_DIFF
kylin.storage.hbase.block-size-bytes:默认值为 1048576
kylin.storage.hbase.small-family-block-size-bytes:指定 Block 大小,默认值为 65536(byte),即 64(KB)
kylin.storage.hbase.owner-tag:指定 Kylin 平台的所属人,默认值为 whoami@kylin.apache.org
kylin.storage.hbase.endpoint-compress-result:是否返回压缩结果,默认值为 TRUE
kylin.storage.hbase.max-hconnection-threads:指定连接线程数量的最大值,默认值为 2048
kylin.storage.hbase.core-hconnection-threads:指定核心连接线程的数量,默认值为 2048
kylin.storage.hbase.hconnection-threads-alive-seconds:指定线程存活时间,默认值为 60
kylin.storage.hbase.replication-scope:指定集群复制范围,默认值为 0
kylin.storage.hbase.scan-cache-rows:指定扫描缓存行数,默认值为 1024
代码语言:javascript复制
JVM参数调优(在HBase的env脚本中进行调整)
export HBASE_OPTS="-verbose:gc -XX: PrintGCDetails -Xloggc:${HBASE_LOG_DIR}/hbase-gc.log -XX: PrintGCTimeStamps -XX: PrintGCApplicationConcurrentTime -XX: PrintGCApplicationStoppedTime 
-server -Xmx20480m -Xms20480m -Xmn10240m -Xss256k  -XX:SurvivorRatio=4 -XX:MaxPermSize=256m -XX:MaxTenuringThreshold=15 
-XX:ParallelGCThreads=16 -XX: UseConcMarkSweepGC -XX: UseParNewGC  -XX:CMSFullGCsBeforeCompaction=5 -XX: UseCMSCompactAtFullCollection 
-XX: CMSClassUnloadingEnabled  -XX:CMSInitiatingOccupancyFraction=70 -XX: UseCMSInitiatingOccupancyOnly -XX:CMSMaxAbortablePrecleanTime=5000     

Kylin的参数调整方面,有关cube设计、大小、以及合并等参数:

代码语言:javascript复制
数据类型精度(一般不做修改):
kylin.source.hive.default-varchar-precision:指定 varchar 字段的最大长度,默认值为256
kylin.source.hive.default-char-precision:指定 char 字段的最大长度,默认值为 255
kylin.source.hive.default-decimal-precision:指定 decimal 字段的精度,默认值为 19
kylin.source.hive.default-decimal-scale:指定 decimal 字段的范围,默认值为 4
代码语言:javascript复制
Cube 设计:

kylin.cube.ignore-signature-inconsistency:Cube desc 中的 signature 信息能保证 Cube 不被更改为损坏状态,默认值为 FALSE
kylin.cube.aggrgroup.max-combination:指定一个 Cube 的聚合组 Cuboid 上限,默认值为 32768
kylin.cube.aggrgroup.is-mandatory-only-valid:是否允许 Cube 只包含 Base Cuboid,默认值为 FALSE,当使用 Spark Cubing 时需设置为 TRUE
kylin.cube.rowkey.max-size:指定可以设置为 Rowkeys 的最大列数,默认值为 63,且最大不能超过 63
kylin.cube.allow-appear-in-multiple-projects:是否允许一个 Cube 出现在多个项目中
kylin.cube.gtscanrequest-serialization-level:默认值为 1
kylin.metadata.dimension-encoding-max-length:指定维度作为 Rowkeys 时使用 fix_length 编码时的最大长度,默认值为 256
kylin.web.hide-measures: 隐藏一些可能不需要的度量,默认值是RAW
代码语言:javascript复制
Cube 大小估计:

kylin.cube.size-estimate-ratio:普通的 Cube,默认值为 0.25
kylin.cube.size-estimate-memhungry-ratio:已废弃,默认值为 0.05
kylin.cube.size-estimate-countdistinct-ratio:包含精确去重度量的 Cube 大小估计,默认值为 0.5
kylin.cube.size-estimate-topn-ratio:包含 TopN 度量的 Cube 大小估计,默认值为 0.5
代码语言:javascript复制
Cube 构建算法:

kylin.cube.algorithm:指定 Cube 构建的算法,参数值可选 auto,layer 和 inmem, 默认值为 auto,即 Kylin 会通过采集数据动态地选择一个算法 (layer or inmem),如果用户很了解 Kylin 和自身的数据、集群,可以直接设置喜欢的算法
kylin.cube.algorithm.layer-or-inmem-threshold:默认值为 7
kylin.cube.algorithm.inmem-split-limit:默认值为 500
kylin.cube.algorithm.inmem-concurrent-threads:默认值为 1
kylin.job.sampling-percentage:指定数据采样百分比,默认值为 100
代码语言:javascript复制
Cube 构建:

kylin.storage.default:指定默认的构建引擎,默认值为 2,即 HBase
kylin.source.hive.keep-flat-table:是否在构建完成后保留 Hive 中间表,默认值为 FALSE
kylin.source.hive.database-for-flat-table:指定存放 Hive 中间表的 Hive 数据库名字,默认值为 default,请确保启动 Kylin 实例的用户有操作该数据库的权限
kylin.source.hive.flat-table-storage-format:指定 Hive 中间表的存储格式,默认值为 SEQUENCEFILE
kylin.source.hive.flat-table-field-delimiter:指定 Hive 中间表的分隔符,默认值为 u001F
kylin.source.hive.redistribute-flat-table:是否重分配 Hive 平表,默认值为 TRUE
kylin.source.hive.redistribute-column-count:重分配列的数量,默认值为 3
kylin.source.hive.table-dir-create-first:默认值为 FALSE
kylin.storage.partition.aggr-spill-enabled:默认值为 TRUE
kylin.engine.mr.lib-dir:指定 MapReduce 任务所使用的 jar 包的路径
kylin.engine.mr.reduce-input-mb:MapReduce 任务启动前会依据输入预估 Reducer 接收数据的总量,再除以该参数得出 Reducer 的数目,默认值为 500(MB)
kylin.engine.mr.reduce-count-ratio:用于估算 Reducer 数目,默认值为 1.0
kylin.engine.mr.min-reducer-number:MapReduce 任务中 Reducer 数目的最小值,默认值为 1
kylin.engine.mr.max-reducer-number:MapReduce 任务中 Reducer 数目的最大值,默认值为 500
kylin.engine.mr.mapper-input-rows:每个 Mapper 可以处理的行数,默认值为 1000000,如果将这个值调小,会起更多的 Mapper
kylin.engine.mr.max-cuboid-stats-calculator-number:用于计算 Cube 统计数据的线程数量,默认值为 1
kylin.engine.mr.build-dict-in-reducer:是否在构建任务 Extract Fact Table Distinct Columns 的 Reduce 阶段构建字典,默认值为 TRUE
kylin.engine.mr.yarn-check-interval-seconds:构建引擎间隔多久检查 Hadoop 任务的状态,默认值为 10(s)

优化实战(二):Cube的高级设置

随着维度数目的增加,Cuboid 的数量会爆炸式地增长。为了缓解 Cube 的构建压力,Apache Kylin 引入了一系列的高级设置,帮助用户筛选出真正需要的 Cuboid。这些高级设置包括聚合组(Aggregation Group)、联合维度(Joint Dimension)、层级维度(Hierachy Dimension)和必要维度(Mandatory Dimension)等。”

众所周知,Apache Kylin 的主要工作就是为源数据构建 N 个维度的 Cube,实现聚合的预计算。理论上而言,构建 N 个维度的 Cube 会生成 2N 个 Cuboid, 如图 1 所示,构建一个 4 个维度(A,B,C, D)的 Cube,需要生成 16 个Cuboid。

图一

随着维度数目的增加 Cuboid 的数量会爆炸式地增长,不仅占用大量的存储空间还会延长 Cube 的构建时间。为了缓解 Cube 的构建压力,减少生成的 Cuboid 数目,Apache Kylin 引入了一系列的高级设置,帮助用户筛选出真正需要的 Cuboid。这些高级设置包括聚合组(Aggregation Group)、联合维度(Joint Dimension)、层级维度(Hierachy Dimension)和必要维度(Mandatory Dimension)等,本系列将深入讲解这些高级设置的含义及其适用的场景。

聚合组(Aggregation Group)

用户根据自己关注的维度组合,可以划分出自己关注的组合大类,这些大类在 Apache Kylin 里面被称为聚合组。例如图 1 中展示的 Cube,如果用户仅仅关注维度 AB 组合和维度 CD 组合,那么该 Cube 则可以被分化成两个聚合组,分别是聚合组 AB 和聚合组 CD。如图 2 所示,生成的 Cuboid 数目从 16 个缩减成了 8 个。

图二

用户关心的聚合组之间可能包含相同的维度,例如聚合组 ABC 和聚合组 BCD 都包含维度 B 和维度 C。这些聚合组之间会衍生出相同的 Cuboid,例如聚合组 ABC 会产生 Cuboid BC,聚合组 BCD 也会产生 Cuboid BC。这些 Cuboid不会被重复生成,一份 Cuboid 为这些聚合组所共有,如图 3 所示。

图三

有了聚合组用户就可以粗粒度地对 Cuboid 进行筛选,获取自己想要的维度组合。

联合维度(Joint Dimension)

用户有时并不关心维度之间各种细节的组合方式,例如用户的查询语句中仅仅会出现 group by A, B, C,而不会出现 group by A, B 或者 group by C 等等这些细化的维度组合。这一类问题就是联合维度所解决的问题。例如将维度 A、B 和 C 定义为联合维度,Apache Kylin 就仅仅会构建 Cuboid ABC,而 Cuboid AB、BC、A 等等Cuboid 都不会被生成。最终的 Cube 结果如图4所示,Cuboid 数目从 16 减少到 4。

图四

层级维度(Hierarchy Dimension)

用户选择的维度中常常会出现具有层级关系的维度。例如对于国家(country)、省份(province)和城市(city)这三个维度,从上而下来说国家/省份/城市之间分别是一对多的关系。也就是说,用户对于这三个维度的查询可以归类为以下三类:

  • group by country
  • group by country, province(等同于group by province)
  • group by country, province, city(等同于 group by country, city 或者group by city)

以图5所示的 Cube 为例,假设维度 A 代表国家,维度 B 代表省份,维度 C 代表城市,那么ABC 三个维度可以被设置为层级维度,生成的Cube 如图5所示。

图五

例如,Cuboid [A,C,D]=Cuboid[A, B, C, D],Cuboid[B, D]=Cuboid[A, B, D],因而 Cuboid[A, C, D] 和 Cuboid[B, D] 就不必重复存储。

图6展示了 Kylin 按照前文的方法将冗余的Cuboid 剪枝从而形成图 2 的 Cube 结构,Cuboid 数目从 16 减小到 8。

图六

必要维度 (Mandatory Dimension)

用户有时会对某一个或几个维度特别感兴趣,所有的查询请求中都存在group by这个维度,那么这个维度就被称为必要维度,只有包含此维度的Cuboid会被生成(如图7)。

图七

以图 1中的Cube为例,假设维度A是必要维度,那么生成的Cube则如图8所示,维度数目从16变为9。

图八

在Kylin的高级设置中,用户可以根据查询需求对Cube构建预计算的结果进行优化(剪枝),从而减少占用的存储空间。而优化得当的Cube可以在占用尽量少的存储空间的同时提供极强的查询性能。

优化实战(三):Kylin官方案例详细剖析及剪枝优化

Kylin官方案例是一个非常经典的案例,包含的技术细节通过深挖能呈现出更具价值的信息,如:数据仓库理论,星型模型,雪花模型,角色扮演维度,维度剪枝等。官方案例是一个订单案例,其中包含了订单事实表,订单日期表,商品分类维度,账号维度(购买者和销售者)以及区域维度(购买者和销售者)。

一、Kylin官方案例表关系及字段详解

Kylin官方案例表简要说明
  • KYLIN_SALES是事实表,保存了销售订单的明细信息。各列分别保存着卖家、商品分类、订单金额、商品数量等信息,每一行对应着一笔交易订单。
  • KYLIN_CATEGORY_GROUPINGS是维表,保存了商品分类的详细介绍,例如商品分类名称等。
  • KYLIN_CAL_DT也是维表,保存了时间的扩展信息。如单个日期所、月始、周始、年份、月份等。
  • KYLIN_ACCOUNT也是维表,作为角色扮演维度,包含购买者和开发者账号。
  • KYLIN_COUNTRY包含区域维度,作为角色扮演维度,包含购买者和开发者所在区域信息。
KYLIN_SALES 事实表字段详解:
KYLIN_CATEGORY_GROUPINGS主要字段:
  • LEAF_CATEG_ID:主键
  • SITE_ID:外键
KYLIN_CATEGORY_GROUPINGS 对外连接关系:
KYLIN_CATEGORY_GROUPINGS 主要字段解释:
KYLIN_COUNTRY 主要字段详解:
KYLIN_ACCOUNT 主要字段详解:
雪花模型关系总览:

二、建立模型(Model)

Kylin 官方模型 model关系图如下:
第一步:雪花和星型模型,建立inner join关系,重要的是确定字段关联,对于角色维度(KYLIN_ACCOUNT和KYLIN_COUNTRY),需要别名处理。

对 “Add Lookup Table” 页面的几点说明:

1.数据关系不仅仅是事实表与维度表之间(星型模型),维度表和维度表之间(雪花模型)也可以建立联系; 2.表与表之间的连接添加有三种:“Left Join”、“Inner Join”、“Right Join”; 3.Skip snapshot for this lookup table 选项指的是是否跳过生成 snapshotTable,由于某些 Lookup 表特别大(大于 300M),如果某一个维度的基数比较大 ,可能会导致内存出现 OOM,所以在创建 snapshotTable 的时候会限制原始表的大小不能超过配置的一个上限值(kylin.snapshot.max-mb,默认值300); 4.跳过构建 snapshot 的 lookup 表将不能搜索,同时不支持设置为衍生维度(Derived); 5.大部分情况下都是使用 “Left Join”,其他两种 Join 方式不是很常用。

每一个 Snapshot 是和一个 Hive 维度表对应的,生成的过程是:

从原始的hive维度表中顺序得读取每一行每一列的值;

使用 TrieDictionary 方式对这些所有的值进行编码(一个值对应一个 Id);

再次读取原始表中每一行的值,将每一列的值使用编码之后的 Id 进行替换,得到了一个只有 Id 的新表;

同时保存这个新表和 Dictionary 对象(Id 和值的映射关系)就能够保存整个维度表;

Kylin 将这个数据存储到元数据库中。

雪花模型关系图
第二步:资格维度选择:

在 Dimensions 页面选择可能参与计算的维度,这里被选择的只是在 Cube 构建的时候拥有被选择资格的维度,并不是最后参与 Cube 构建的维度,推荐将维度表中的字段都选择上。

如下展示了Dimensions的选择:

  • 对于KYLIN_SALES,其中SLR_SEGMENT_CD,PRICE,ITEM_COUNT没有选择,所以没有资格参与cubeId构建
  • 对于KYLIN_CAL_DT,其中仅选择了部分参与的维度,其他没有资格参与cubeId构建
  • 对于KYLIN_CATEGORY_GROUPING,其中仅选择了部分参与的维度,其他没有资格参与cubeId构建
  • 对于BUYER_ACCOUNT 与 SELLER_ACCOUNT,全部有资格参与cubeId构建
  • 对于BUYER_COUNTRY,只有COUNTRY , NAME 有资格参与cubeId构建
  • 对于SELLER_COUNTRY,只有COUNTRY , NAME 有资格参与cubeId构建
资格维度选择结果总览:
第三步:Measures 度量指标选择:

在 Measures 页面选择可能用于计算的度量。一般而言,销售额、流量、温湿度等会作为度量。

第四步:Settings设置

在 Settings 页面可以设置分区以及过滤条件,其中分区是为了系统可以进行增量构建而设计的,目前 Kylin 支持基于日期的分区,在 “Partition Date Column” 后面选择事实表或者维度表中的日期字段,然后选择日期格式即可;过滤条件设置后,Kylin 在构建的时候会选择符合过滤条件的数据进行构建。

需要注意的几点:

1.时间分区列可以支持日期或更细粒度的时间分区; 2.时间分区列支持的数据类型有 time/date/datetime/integer等; 3.过滤条件不需要写 WHERE; 4.过滤条件不能包含日期维度。

三、构建cube模型

维度选择

在选择维度时,每一个维度列可以作为普通维度(Normal),也可以作为衍生维度(Derived)。相对于普通维度来说,衍生维度并不参与维度的 Cuboid,衍生维度对应的外键(FK)参与维度 Cuboid,从而降低 Cuboid 数。在查询时,对衍生维度的查询会首先转换为对外键所在维度的查询,因此会牺牲少量性能(大部分情况下可以接受)。

维度剪枝优化

如何进行维度优化,首先请确认你设置的cube维度都是你查询时会使用到的。

目前Kylin可以使用的维度优化手段有以下几种:

  • 聚集组
  • 衍生纬度
  • 强制维度
  • 层次维度
  • 联合维度
  • Extended Column

在一个多维数据集合中,维度的个数决定着维度之间可能的组合数,而每一个维度中成员集合的大小决定着每一个可能的组合的个数,例如有三个普通的维度A、B、C,他们的不同成员数分别为10/100/1000,那么一个维度的组合有2的3次方个,分别是{空、A、B、C、AB、BC、AC、ABC},每一个成员我们称为cuboid(维度的组合),而这些集合的成员组合个数分别为1、10、100、1000、10*100、100 1000、101000和10 *100 *1000。我们称每一个dimension中不同成员个数为cardinatily,我们要尽量避免存储cardinatily比较高的维度的组合。

在上面的例子中我们可以不缓存BC和C这两个cuboid,可以通过计算的方式通过ABC中成员的值计算出BC或者C中某个成员组合的值,这相当于是时间和空间的一个权衡吧。在kylin中存在的四种维度是为了减少cuboid的个数,而不是每一个维度是否缓存的,当前kylin是对所有的cuboid中的所有组合都进行计算和存储的,对于普通的dimension,从上面的例子中可以看出N个维度的cuboid个数为2的N次方,而kylin中设置了一些维度可以减少cuboid个数,当然,这需要使用者对自己需要的维度十分了解,知道自己可能根据什么进行group by。

Mandatory维度

这种维度意味着每次查询的group by中都会携带的,将某一个dimension设置为mandatory可以将cuboid的个数减少一半,如下图:

这是因为我们确定每一次group by都会携带A,那么就可以省去所有不包含A这个维度的cuboid了。

hierarchy维度

这种维度是最常见的,尤其是在mondrian中,我们对于多维数据的操作经常会有上卷下钻之类的操作,这也就需要要求维度之间有层级关系,例如国家、省、城市,年、季度、月等。有层级关系的维度也可以大大减少cuboid的个数。如下图:

这里仅仅局限于A/B/C是一个层级,例如A是年份,B是季度、C是月份,那么查询的时候可能的组合只有年、xx年的季度、xx年xx季度的xx月,这就意味着我们不能再单独的对季度和月份进行聚合了,例如我们查询的时候不能使用group by month,而必须使用group by year,quart,month。如果需要单独的对month进行聚合,那么还需要再使用month列定义一个单独的普通维度。

derived维度

这类维度的意思是可推导的维度,需要该维度对应的一个或者多个列可以和维度表的主键是一对一的,这种维度可以大大减少cuboid个数,如下图:

联合维度(Joint)

联合维度:将几个维度视为一个维度。

适用场景:

1.可以将确定在查询时一定会同时使用的几个维度设为一个联合维度。 2.可以将基数很小的几个维度设为一个联合维度。 3.可以将查询时很少使用的几个维度设为一个联合维度。

优化效果:将N个维度设置为联合维度,则这N个维度组合成的cuboid个数会从2的N次方减少到1。

粒度优化

粒度优化对应的是提高Cube的并发度,其设置是在自定义属性中的 一共有三个属性可以提高并发度。

1.kylin.hbase.region.cut(共使用几个分区) 2.kylin.hbase.region.count.min(最少使用几个分区) 3.kylin.hbase.region.count.max(最多使用几个分区)

根据相对应的情况调高最少使用分区,降低最大使用分区,能够有效增加系统的并行度。

RowKey优化

Rowkeys: 是由维度编码值组成。”Dictionary” (字典)是默认的编码方式; 字典只能处理中低基数(少于一千万)的维度;如果维度基数很高(如大于1千万), 选择 “false” 然后为维度输入合适的长度,通常是那列的最大长度值; 如果超过最大值,会被截断。请注意,如果没有字典编码,cube 的大小可能会非常大。

你可以拖拽维度列去调整其在 rowkey 中位置; 位于rowkey前面的列,将可以用来大幅缩小查询的范围。通常建议将 mandantory 维度放在开头, 然后是在过滤 ( where 条件)中起到很大作用的维度;如果多个列都会被用于过滤,将高基数的维度(如 user_id)放在低基数的维度(如 age)的前面。

Kylin 以 Key-Value 的方式将 Cube 存储到 HBase 中,HBase 的 key,也就是 Rowkey,是由各维度的值拼接而成的;为了更高效地存储这些值,Kylin 会对它们进行编码和压缩;每个维度均可以选择合适的编码(Encoding)方式,默认采用的是字典(Dictionary)编码技术;字段支持的基本编码类型如下:

  • dict:适用于大部分字段,默认推荐使用,但在超高基情况下,可能引起内存不足的问题;对于使用该种编码的维度,每个Segment在构建的时候都会为这个维度所有可能的值创建一个字典,然后使用字典中每个值的编号来编码。Dict的优势是产生的编码非常紧凑,尤其在维度值的基数较小且长度较大的情况下,特别节约空间。由于产生的字典是在查询时加载入构建引擎和查询引擎的,所以在维度的基数大、长度也大的情况下,容易造成构建引擎或查询引擎的内存溢出。
  • boolean:适用于字段值为true, false, TRUE, FALSE, True, False, t, f, T, F, yes, no, YES, NO, Yes, No, y, n, Y, N, 1, 0;
  • integer:适用于字段值为整数字符,支持的整数区间为[ -2^(8N-1), 2^(8N-1)];Integer编码需要提供一个额外的参数“Length”来代表需要多少个字节。Length的长度为1~8。如果用来编码int32类型的整数,可以将Length设为4;如果用来编码int64类型的整数,可以将Length设为8。在更多情况下,如果知道一个整数类型维度的可能值都很小,那么就能使用Length为2甚至是1的int编码来存储,这将能够有效避免存储空间的浪费。
  • date:适用于字段值为日期字符,支持的格式包括yyyyMMdd、yyyy-MM-dd、yyyy-MM-dd HH:mm:ss、yyyy-MM-dd HH:mm:ss.SSS,其中如果包含时间戳部分会被截断;将日期类型的数据使用三个字节进行编码,其支持从0000-01-01到9999-01-01中的每一个日期。
  • time:适用于字段值为时间戳字符,支持范围为[ 1970-01-01 00:00:00, 2038/01/19 03:14:07],毫秒部分会被忽略,time编码适用于 time, datetime, timestamp 等类型;Time编码仅仅支持到秒。但是Time编码的优势是每个维度仅仅使用4个字节,这相比普通的长整数编码节约了一半。如果能够接受秒级的时间精度,请选择Time编码来代表时间的维度。
  • fix_length:适用于超高基场景,将选取字段的前 N 个字节作为编码值,当 N 小于字段长度,会造成字段截断,当 N 较大时,造成 RowKey 过长,查询性能下降,只适用于 varchar 或 nvarchar 类型;编码需要提供一个额外的参数“Length”来代表需要多少个字节。该编码可以看作Dict编码的一种补充。对于基数大、长度也大的维度来说,使用Dict可能不能正常工作,于是可以采用一段固定长度的字节来存储代表维度值的字节数组,该数组为字符串形式的维度值的UTF-8字节。如果维度值的长度大于预设的Length,那么超出的部分将会被截断。
  • fixed_length_hex:适用于字段值为十六进制字符,比如 1A2BFF 或者 FF00FF,每两个字符需要一个字节,只适用于 varchar 或 nvarchar 类型。
  • 和Hbase 的RowKey优化类似,在查询的过程中,被用作过滤条件的维度可能放在其他维度的前面,经常出现的维度应该放在前面,基数比较大的维度应该放在前面

Kylin 大数据下的OLAP解决方案和行业典型应用

Apache Kylin在百度地图的实践

对于 Apache Kylin 在实际生产环境中的应用,在国内,百度地图数据智能组是最早的一批实践者之一。目前,百度地图大数据 OLAP 多维分析平台承载百度地图内部多个基于 Apache Kylin 引擎的亿级多维分析查询项目,共计约 80 个 cube,平均半年时间的历史数据,共计约 50 亿行的源数据规模,单表最大数据量为 20 亿 条源数据,满足大时间区间、复杂条件过滤、多维汇总聚合的单条 SQL 查询毫秒级响应,较为高效地解决了亿级大数据交互查询的性能需求。

Kylin 有效解决的痛点问题:

  • 痛点一:百亿级海量数据多维指标动态计算耗时问题,Apache Kylin 通过预计算生成 Cube 结果数据集并存储到 HBase 的方式解决。
  • 痛点二:复杂条件筛选问题,用户查询时,Apache Kylin 利用 router 查找算法及优化的 HBase Coprocessor 解决;
  • 痛点三:跨月、季度、年等大时间区间查询问题,对于预计算结果的存储,Apache Kylin 利用 Cube 的 Data Segment 分区存储管理解决。

百度地图大数据 OLAP 平台系统架构

主要模块包括:

  • 数据接入:主要负责从数据仓库端获取业务所需的最细粒度的事实表数据。
  • 任务管理:主要负责Cube 的相关任务的执行、管理等。
  • 任务监控:主要负责Cube 任务在执行过程中的状态及相应的操作管理。
  • 集群监控:主要包括Hadoop 生态进程的监控及Kylin 进程的监控。

基于仓库端 join 好的 fact 事实表建 Cube,减少对小规模集群带来的 hive join 压力

对于 Cube 的设计,官方有专门的相关文档说明,里面有较多的指导经验,比如: cube 的维度最好不要超过 15 个, 对于 cardinality 较大的维度放在前面,维度的值不要过大,维度 Hierarchy 的设置等等。

实践中,百度地图将某个产品需求分为多个页面进行开发,每个页面查询主要基于事实表建的 cube,每个页面对应多张维度表和 1 张事实表,维度表放在 MySQL 端,由数据仓库端统一管理,事实表计算后存放在 HDFS 中,事实表中不存储维度的名称,仅存储维度的 id,主要基于 3 方面考虑:

  • 第一:减少事实表体积
  • 第二:由于我们的 Hadoop 集群是自己单独部署的小集群,MapReduce 计算能力有限,join 操作希望在仓库端完成,避免给 Kylin 集群带来的 Hive join 等计算压力
  • 第三:减少回溯代价

假设我们把维度名称也存在 Cube 中,如果维度名称变化必然导致整个 cube 的回溯,代价很大。这里可能有人会问,事实表中只有维度 id 没有维度 name,假设我们需要 join 得到查询结果中含有维度 name 的记录,怎么办呢?对于某个产品的 1 个页面,我们查询时传到后台的是维度 id,维度 id 对应的维度 name 来自 MySQL 中的维度表,可以将维度 name 查询出来并和维度 id 保存为 1 个维度 map 待后续使用。同时,一个页面的可视范围有限,查询结果虽然总量很多,但是每一页返回的满足条件的事实表记录结果有限,那么,我们可以通过之前保存的维度 map 来映射每列 id 对应的名称,相当于在前端逻辑中完成了传统的 id 和 name 的 join 操作。

Aggregation cube 辅助中高维度指标计算,解决向上汇总计算数据膨胀问题

比如我们的事实表有个 detail 分区数据,detail 分区包含最细粒度 os 和 appversion 两个维度的数据 (注意: cuid 维度的计算在仓库端处理),我们的 cube 设计也选择 os 和 appversion,hierarchy 层次结构上,os 是 appversion 的父亲节点,从 os appversion(group by os, appversion) 组合维度来看,统计的用户量没有问题,但是按照 os(group by os) 单维度统计用户量时,会从基于这个 detail 分区建立的 cube 向上汇总计算,设上午用户使用的是 android 8.0 版本,下午大量用户升级到 android 8.1 版本,android 8.0 组合维度 android 8.1 组合维度向上计算汇总得到 os=android(group by os, where os=android) 单维度用户,数据会膨胀且数据不准确。因此我们为事实表增加一个 agg 分区,agg 分区包含已经从 cuid 粒度 group by 去重后计算好的 os 单维度结果。这样,当用户请求 os 维度汇总的情况下,Apache Kylin 会根据 router 算法,计算出符合条件的候选 cube 集合,并按照权重进行优选级排序 (熟悉 MicroStrategy 等 BI 产品的同学应该知道这类案例),选择器会选中基于 agg 分区建立的 os 单维度 agg cube,而不从 detail 这个分区建立的 cube 来自底向上从最细粒度往高汇总,从而保证了数据的正确性。

新增留存类分析,如何更高效更新历史记录

对应小规模集群,计算资源是非常宝贵的,假设我们对于某个项目的留存分析到了日对 1 日到日对 30 日,日对 1 周到日对 4 周,日对 1 月到日对 4 月,周对 1 周到周对 4 周,月对 1 月到月对 4 月。那么对于传统的存储方案,我们将遇到问题。

假如今天是 2015-12-02,我们计算实际得到的是 2015-12-01 的数据。

此方案的思路是,当今天是2015-12-02,实际是2015-12-01 的数据,如上示例存储,但日对第n 日的留存表示的是n 日前对应的那个日期的留存量,相当于旋转了红色对角线。

此方案的优势:

1.如果要查看某个时间范围内的某 1 个指标,直接选择该范围的该列指标即可 2.如果今后增加新的留存,比如半年留存,年留存等指标,不需要级联更新历史天数的数据,只需要更新 2015-12-01 这 1 天的数据,时间复杂度 O(1) 不变,对物理机器资源要求不高。

此方案的缺点:

1.如果涉及到某 1 天或者某个时间范围的多列指标查询,需要前端开发留存分析特殊处理逻辑,根据相应的时间窗口滑动,从不同的行,选择不同的列,然后渲染到前端页面。

Apache Kylin在链家的实践

链家Kylin平台的架构如图:

如上,为链家 Olap 平台结构,于 16 年底搭建。Kylin 采用集群部署模式,共部署 6 台机器,3 台用于分布式构建 Cube,3 台用于负载均衡查询,query 单台可用内存限制在 80G。同时,计算集群一旦运行大任务,内存压力大的时候,HBase 就会性能非常差,为避免和计算集群互相影响,Kylin 集群依赖独立的 Hbase 集群。同时,对 Hbase 集群做了相应的优化,包括:读写分离、SSD_FIRST 优先读取远程 SSD、并对依赖的 hdfs 做了相应优化。

由于 Kylin 只专注预计算,不保存明细数据,对于即席查询和明细查询,通过自研 QE 引擎实现,底层依赖 spark、presto、hive,通过特定规则,路由到相应查询引擎执行查询。多维分析查询,由 Kylin 集群提供查询服务,可实现简单的实时聚合计算。

当前 Kylin 主要查询方为指标 API 平台,能根据查询 sql 特征,做相应缓存。指标 API 作为数据统一出口,衍生出其他一些业务产品。使用统计,如下:Cube 数量 500 ,覆盖公司 12 个业务线。Cube 存储总量 200 TB,数据行万亿级,单 Cube 最大 40 亿行。日查询量 27 万 ,缓存不命中情况下,时延 < 500ms(70%), < 1s(90%),少量复杂 sql 查询耗时 10s 左右。

当前,kylin 在用版本为 1.6,最新版本为 2.3。自 2.0 版本之后,又新增了一些新的特性,配置文件和属性也做了一些调整。由于,Cube 数据量大,涉及业务方多,在当前无明显瓶颈的情况下,没有实时更新新版本。但是,引入了 2.0 新增的一些重要特性,如分布式构建和分布式锁。

链家维护了自己的一套 Kylin 代码,使用过程中,针对特定场景的进行一些优化开发,包括:

支持分布式构建。原生 kylin 是只能有一台机器进行构建。的当 kylin 上的 cube 越来越多,单台机器显然不能满足任务需求,除了任务数据有限制,任务多时也会互相影响数据构建的效率。通过修改 kylin 的任务调度策略,支持了多台机器同时构建数据。使 kylin 的构建能力可以横向扩展,来保证数据构建;

优化构建时字典下载策略。原生 kylin 在 build cubiod data 时用的字典,会将该字段的全部字典下载到节点上,当字段的字典数量很多或者字典文件很大时,会在文件传输上消耗很多不必要的时间。通过修改代码,使任务只下载需要的字典文件,从而减少文件传输时间消耗,加快构建;

全局字典锁,在同一 Cube 所任务构建时,由于共享全局字典锁,当某执行任务异常时,会导致其他任务获取不到锁,此 bug 已修复并提交官方;

支持设置 Cube 强制关联维表,过滤事实表中无效的维度数据。kylin 创建的临时表作为数据源。当使用 olap 表和维表关联字段作为维度时,会默认不关联维表,直接使用 olap 中的字段做维度。而在 Build Cube 这一步又会使用维表的字典来转换维度的值。如果 olap 中的值维表中没有就会产生问题。我们通过增加配置项,可以使 kylin 强制关联维表,来过滤掉 olap 表中的脏数据;

Kylin query 机器,查询或者聚合,会加载大量的数据到内存,内存占用大,甚至存在频繁 Full GC 的情况。这种情况下,CMS 垃圾回收表现不是很好,因此更换为 G1 收集器,尽量做到 STW 时间可控,并及时调优。

Kylin在滴滴OLAP引擎中的应用

下图为 Kylin 在滴滴 OLAP 引擎中的部署情况,Kylin 集群包含 2 台分布式构建节点、8 台查询节点,其中 2 台查询节点作为集群接口承接 REST 请求,REST 请求主要包含两类:构建作业状态查询和创建类操作,创建类操作如装载表、建模、创建立方体以及对等的删除操作等等。对于构建作业状态查询轮询请求两台节点,而对创建类操作则请求其中固定的一台节点,另一台作为 Standby 存在,这样设计的主要目的是避免集群接口的单点问题,同时解决因 Kylin 集群元数据同步机制导致的可能出现的创建类操作失败问题。

Kylin 作为固化分析场景引擎,主要负责对有聚合缓存需求的表进行查询加速。什么样的表会有这样的需求呢?

  • 报表类产品使用的表
  • 经 OLAP 引擎数据转移决策识别认为需要进行聚合缓存的表

前者不难理解,后者则如引擎中的表,表数据规模较大,且被频繁执行某种聚合分析,在一段时间内达到一定的频次,引擎会识别并认为该表需要执行聚合缓存,进而触发调度将数据“复制”到 Kylin。这样,下次针对该表的聚合分析如果可被 Kylin 的聚合缓存覆盖,就会直接查询 Kylin 中的聚合数据“副本”而非原始的明细数据“副本”。

Apache Kylin 在场景引擎中的使用效果

目前,Kylin 集群维护了700 的立方体,每日运行2000 的构建作业,平均构建时长37 分钟,立方体存储总量30 TB(已去除HDFS 副本影响);对未使用缓存的查询进行统计,80% 的查询耗时小于500ms,90% 的查询耗时小于2.8 秒。需要说明的是,由于OLAP 引擎中“数据转移决策”模块会根据查询场景触发数据“复制”到Kylin 中来,在近期的统计中,立方体数量一度会达到1100 。

在业务方面,Kylin 间接服务了下游数易(面向全公司的报表类数据产品)、开放平台(面向全公司的查询入口)等多个重要数据产品,覆盖了快车、专车等十多个业务线的分析使用,间接用户3000 ,Kylin 的引入为用户提供了稳定、可靠、高效的固化分析性能。

使用 Apache Kylin 遇到的挑战

滴滴使用 Kylin 的方式与传统方式有异,Kylin 在架构设计上与业务紧耦合,传统方式中业务分析人员基于 Kylin 建模、构建立方体(Cube),然后执行分析查询。但滴滴将 Kylin 作为固化分析场景下的引擎使用,提供针对表的聚合缓存服务,这样作为一个通用数据组件的 Kylin 就剥离了业务属性,且与用户相割裂,对外透明。

在最初的使用中,由于没有控制 OLAP 引擎的内部并发,来自调度的聚合缓存任务会在某些情况下高并发地执行 Kylin 的表加载、模型和立方体的创建,因为 Kylin Project 元数据的更新机制导致操作存在失败的可能。当前,我们通过在 OLAP 引擎内部使用队列在一定程度上缓解了问题的发生,此外,结合重试机制,基本可以保证操作的成功完成。最后我们也注意到,该问题在最新的 Kylin 版本中已经进行了修复。

另外,Kylin 默认地,在删除立方体时不会卸载 HBase 中的 Segment 表,而需定期执行脚本进行清理。这样,就导致引擎运行时及时卸载无效的立方体无法级联到 HBase,给 HBase 造成了较大的运维压力。因此我们也对源码进行了调整,在立方体删除时增加了 HBase Segment 表清理的功能等等。

0 人点赞