Kylin构建及调优基础知识

    技术2022-07-11  81

    文章目录

    1. Kylin设计核心思想2. Kylin基本架构3. 基本概念4. Cube构建及优化4.1 Cube构建及优化总览4.2 Cube构建分步骤说明4.2.1 Cube Info4.2.2 Dimensions4.2.3 Measures4.2.4 Refresh Setting 4.3 Cube 构建前优化策略4.3.1 Advanced Settings4.3.1.1 Aggregation Groups4.3.1.2 Rowkeys4.3.1.3 Mandatory Cuboids4.3.1.4 Cubing Engine4.3.1.5 Column Family 4.3.2 Overview Settings 4.4 Cube 构建后优化策略4.4.1 Cube 优化判断指标4.4.2 Cube 构建后优化方法 4.5 一个优化的案例分享

    1. Kylin设计核心思想

    业务的发展伴随着事实表的数据体量的增大,查询时间也越来越长,而另一方面,结果集的数据量却没有显著增长,指标的变化也并不频繁。我们希望所有的查询都能在秒级返回查询结果,于是,我们有了一个思路:为什么不能把所有的结果先算好,查询时直接从结果集中取出来呢?这也就是 kylin 设计的初衷。 kylin的核心思想是预计算。 好,现在这个组件已经有了一个准确的定位,那么接下来要解决的问题就是如何实现预计算,这套架子里究竟需要什么组件呢?

    2. Kylin基本架构

    以下即为 kylin 的插件式架构。 上图中,蓝色的框框是 kylin 系统的边界,里面是 kylin 的核心功能,蓝色框框外面的部分是 Kylin 的外围插件和适配器。

    从模块上来看,大致分为以下几部分:

    Data Source Abstraction:数据源抽象层,目前离线的主流实现是 hive,实时的主流实现是 kafka。Cube Builder:也就是构建引擎,目前支持 MapReduce 和 Spark。Storage Abstraction:结果集存储抽象层,目前主流实现是 hbase,也可以以数据流的方式输出到其他大数据生态工具。Metadata:元数据管理,包括 model,cube,job 等元数据的信息,由于 Kylin 的结果表名和 rowkey 都是经过编码的,必须要通过元数据来映射,所以元数据非常关键,若元数据损坏,相对应的结果集也会不可用,表现为表损坏或查询时报错。Rest Server:通过 Restful API,JDBC/ODBC 的方式向外界提供接口。Query Engine:查询引擎,内置 Apache 开源 SQL 解析工具 Calcite,生成执行计划并执行物理查询计划。Routing:路由转换,实时查询情景下,路由指向数据存储层 Hbase,准实时查询情景下,路由指向数据源层 Hive,准实时情景一般是在结果集的数据满足不了查询需求时,Kylin会转而向数据源做查询,也就是所谓的“查询下压”。

    从数据流上来看,大致分为以下几部分:

    红线:离线任务数据流,从数据源 Hive 读取数据,经计算引擎 MapReduce 或 Spark 计算,生成结果集并存储到 Hbase,离线构建任务可以定时,构建任务可通过 Rest API 定时调度。绿色实线:实时查询数据流,从客户端通过 Rest API 或 JDBC/ODBC,经 Rest Server 和 Query Engine 处理,由路由转发至 Hbase,秒级返回查询结果。绿色虚线:准实时查询数据流,查询下压时,通过数据源直接汇总出查询结果,性能视计算复杂程度而定。

    接下来,我们就要涉及到 Cube 构建的一些概念啦~~

    3. 基本概念

    Cube / Segment / Cuboid :一个查询结果集为一个 Cube; 对于分区表来说,一个日期区间为一个 Segment,一个 Cube 可分为多个 Segment ;每个 Segment 中,有不同维度组合的小结果集,每个小结果集称为一个 Cuboid ,如A维度,B维度,A+B维度,就是3个不同的 Cuboid,同一个Cube 中,不同 Segment 中的 Cuboid 组合和个数都相同。字典:可以把字典理解为维度值向整型数值的映射表,若一个维度值用了字典编码,Hbase 的 rowkey 中就会存储映射后的整数值以提高检索速度,字典内部数据是有序的,方便查找和筛选。字典分为两种:Global Dictionary 和 Segment Dictionary,区别是作用域不同,分别为整个 Cube 或一个 Segment。字典在运算时会加载到内存中,所以,对于一些基数比较高的维度,要慎用字典,防止查询时内存溢出。

    那么,万事俱备,我们开始构建 Cube 吧~

    4. Cube构建及优化

    4.1 Cube构建及优化总览

    构建Cube的过程设计可以说是 kylin 系统设计的精华,这7个步骤封装了 kylin 绝大多数的实现细节,呈现出来的是简洁精炼的构建步骤。 上图中,有7个步骤,可以分为三个阶段:前四步为构建阶段,5、6步为优化阶段,最后一步为总览。下面我们一个一个详细说一下。

    4.2 Cube构建分步骤说明

    4.2.1 Cube Info

    在这一步中,我们需要设置的是 Cube 的基本信息,其中包括:

    Model Name:model 名。Cube Name: Cube 名,全局唯一,否则会创建失败。Notification Email List:发生了 Notification Events 时要通知的邮件列表,逗号分隔。Notification Events:发生什么事件时要邮件通知列表中的用户,分为 Success(构建成功)、Error(构建失败)、Discard(构建中止)。Description:描述。

    4.2.2 Dimensions

    设置 Cube 所需的维度,查找表中的维度可以设置为 normal 或 derived,设置为 derived 的维度不会在 rowkey 中出现,而是用其他 normal 维度通过一层映射得到。

    4.2.3 Measures

    这一步骤主要选择 Cube 中要构建的指标。 所有 Cube 都会计算一个默认的指标 COUNT,即数行数,除此之外,我们还可以定义其他指标:

    SUM:求和。MIN:最小值。MAX:最大值。COUNT:计数。COUNT_DISTINCT:去重计数,有精确计算和模糊计算两种。一般来说,计算越精确,构建时资源消耗越多,查询时响应速度越慢;计算越模糊,构建时资源消耗越少,查询时响应速度越快。精确计算的 COUNT_DISTINCT 会存储为 bitmap,模糊计算的 COUNT_DISTINCT 会存储为其他的数据结构(如HyperLogLog),无论是哪种数据结构,都会占用一定的存储空间,若数据量大,占用的空间可能会在M级别,所以,若有多个去重指标,最好把它们存储在不同的列簇中,防止 Hbase 检索速度过慢。TOP_N:前 N 位。适用于以下场景: select dimA, measure from table_name where dimB = 'b' and dimC = 'c' order by measure limit N;

    在不用 TOP_N 的情况下,查询引擎会从 Hbase 中查出所有符合条件的行,并依据其中的某个指标对所有列做排序。例如下面的场景,经过 TOP_N 优化后,Hbase 中数据存储的形式会从这样:

    Base Cuboid 的 RowKeySUM(PRICE)20120218_00_seller0000001291.5820120218_00_seller0000002365.1820120218_00_seller0000003135.29……20120218_00_seller1000000272.3120120218_01_seller0000001172.52

    变成这样:

    base cuboid 的 RowKeyTop_N Measure20120218_00sellerOO 10091:1092.21, seller0005002:1090.35,…sellerOOO 1789:891.3720120218_01seller0003012:xx.xx20120218 02seller0004001:xx.xx……20120218 50seIler000699: xx.xx

    经 TOP_N 优化后存储结构节省了检索的计算成本,免去了排序的计算成本,可提高查询性能,但带来的问题是列簇的空间占用增加,Cube 的体积会变大。且子 Cuboid 的 TOP_N 指标是从父 TOP_N 的指标聚合出来的,会有一定程度的误差。 7) EXTENDED_COLUMN:引申列。适用于筛选时指定某些 id,但显示时却要显示成 name 时的场景,即把一个维度作为另一个维度的引申列。(后续补充引申列的查询方法和存储结构) 8) PERCENTILE:分位数。查询时该函数的语法与 hive 类似,例如 percentile(measure_a, 0.5),即计算 measure_a 的二分位数,即中位数。

    4.2.4 Refresh Setting

    这一步骤主要是表的刷新机制和生命周期设置,分为以下几项:

    Auto Merge Thresholds:自动合并 Segments,可以设多级。若不合并,查询引擎需要在对各 Segments 查询后,对结果做一个合并。所以合并 Segment 在一定程度上可以提高查询效率。但是 Segment 是 Cube 构建和刷新的最小单位,合并后就无法对原有的某一个 Segment 单独做刷新操作。Volatile Range:最近 N 天的 Segments 不作合并,适用于因数据延迟导致最近 N 天数据会有波动,需要刷新 Segments 的情况。Retention Threshold:生命周期,自动删除 N 天以前的 Segment。Partition Start Date:起始日期,默认 1970-01-01。

    4.3 Cube 构建前优化策略

    前四步走完,相当于 Cube 的构建步骤基本完成了,以下的步骤,更确切地说是对 Cube 的优化,以提升构建速度或查询性能。

    4.3.1 Advanced Settings

    4.3.1.1 Aggregation Groups

    Aggregation Groups 直译过来是聚合组。就是指定 Cube 中哪些维度会同时出现,若 Cube 中有两个维度没有同时出现在任何一个聚合组中,Cube 构建时就不会生成同时包含这两个维度的 Cuboid,聚合组是一个强大的剪枝工具。主要包含以下几项:

    Max Dimension Combination:查询时会涉及到的最大维度个数。设置后,多于该维度个数的 Cuboid 将不会被构建。Includes:聚合组里包含哪些维度。Mandatory Dimensions:在该聚合组中,有哪些维度是查询时必选的。Hierarchy Dimensions:哪些维度之间有层级关系,如 国家 -> 省 -> 市。Joint Dimensions:哪些维度之间几乎是一对一的。 注意:若某维度被设置为 Mandatory Dimensions,该维度将不允许出现在 Hierarchy Dimensions 或 Joint Dimensions 中。

    4.3.1.2 Rowkeys

    rowkey 的优化分为以下几个方面:

    Hbase 中 rowkey 的维度顺序。《Apache Kylin 权威指南》中给出了一个维度评分的公式,维度评分越高,越应该放在前排: 维度打分 = 维度出现在过滤条件中的概率 * 用该维度过滤时可以过滤掉的记录数

    维度编码。维度编码有以下几种: a) boolean:布尔型。 b) dict:字典型,一般把字符串映射为整型,rowkey 中存储映射后的整型数值,字典在运算时会加载到内存中,若维度基数过高,可能会导致内存溢出。此时应考虑换成其他数据编码。 c) fixed_length:定长,若维度值长度不等,编码时会对其进行截断或补位,适用于电话号码等定长维度。 d) fixed_length_hex:16进制定长编码,用途尚不明确。 e) integer:整型编码。

    是否按某维度分片。一般来说,数据分片策略可近似地认为是随机的,若设置时按某维度进行分片,可能会提高查询效率,但如果这个分片的维度基数过高,查询时可能会造成 Hbase 过大的压力,甚至会影响到 Kylin 的可用性。此优化项需谨慎使用。

    官方推荐单台 Hbase 机器的 region 数量在 100 个左右,若超过太多,可能会影响查询性能。

    4.3.1.3 Mandatory Cuboids

    必须出现的 Cuboid,支持文件导入。 还有一种方式可以指定要构建的 Cuboid,就是把这个 Cuboid 中出现的所有维度放到一个聚合组里,再把这些维度全部设置为 Mandatory Dimensions。

    4.3.1.4 Cubing Engine

    目前有两种 Cubing Engine 可供选择:MapReduce 和 Spark,前者速度慢,但稳定,后者速度快,但有内存占用过高,导致系统不稳定的风险。 一般来说,若有 COUNT_DISTINCT 指标,我们会优先考虑稳定性,选择 MapReduce,否则我们优先考虑速度,选择 Spark。

    4.3.1.5 Column Family

    一般来说,COUNT_DISTINCT 指标会单独放到一个列簇里面,其他指标会放到另一个列簇里面。若同时有多个 COUNT_DISTINCT 指标,需要根据每个指标列占用空间的大小,评估是否要把不同的 COUNT_DISTINCT 指标放到不同的列簇当中。

    4.3.2 Overview Settings

    其他自定义配置项,此处省略。

    4.4 Cube 构建后优化策略

    4.4.1 Cube 优化判断指标

    一个足够优化的 Cube,需要包含常查询的 Cuboid,删除不常查询的 Cuboid,即在满足查询性能的前提下,占用的空间足够小,只物化高频查询的 Cuboid。基于这一点,我们有一个指标,称为膨胀率。

    膨胀率 (Expansion Rate) = Cube数据大小 / 源数据大小

    膨胀率一般在0 ~ 1000%,若超过这个阈值,这个 Cube 很可能需要优化。例如下面这个 Cube: 膨胀率已超过1000%,所以我们要对其进行优化。

    4.4.2 Cube 构建后优化方法

    在 Cube 已经构建好以后,又如何进行优化呢? 这时我们就要用到 Cube Planner 这个工具啦,Cube Planner 是内嵌在 Kylin 中的一个剪枝工具,可以统计出每个 Cuboid 的命中次数,进而把各个 Cuboid 做冷热分级,Cube Planner 的统计结果会可视化成下面这张旭日图: 上图中,左边是优化前的 Cube,我们会发现,这个 Cube 之所以膨胀率会超过1000%,是因为它物化了很多从来没被使用过的 Cuboid,右边是推荐的 Cuboid 组合,我们发现 Cube Planner 在优化后会把没被命中过的 Cuboid 删除,优化 Cube 在空间上的占用,还会新增一些 Cuboid,在时间上优化 Cube 的查询性能。 鼠标悬浮在每个 Cuboid 上时,Kylin 还会显示出该 Cuboid 的相关信息:

    Name:各维度是否出现,按 rowkey 排序。ID:cuboid ID。Query Count:命中数,也包括子 Cuboid 没被物化时,由此 Cuboid 计算得出的查询命中数。Exactly Match Count:精确命中数。Row Count:行数。Rollup Rate:子cuboid行数 / 父cuboid行数。

    4.5 一个优化的案例分享

    Processed: 0.008, SQL: 9