kylin性能优化

整体脑图如下:

整体脑图

一. 优化 cube设计

1. 来源优化

数据来源主要来自hive表,所以可以优化hive表

  • 全量的hive表 转换为 分区的hive表,全量变增量
  • 保证hive表没有重复数据
  • hive 输入表中使用 ORC 格式和压缩(Snappy)

2. 输出优化

数据输出主要是使用hbase,所以可以优化hbase表

  • 使用 Snappy 压缩 HBase Cube, 设置方式为
kylin.hbase.default.compression.codec = snappy

3. 参数优化

参数优化有很多参数, 只列举下常用的增加内存的参数, 还有很多参数具体见官方文档

http://kylin.apache.org/cn/docs/install/configuration.html#cube-build

  • MapReduce 任务配置重写
如果用户希望任务从 Yarn 获得更多内存,可以这样设置:
    
kylin.engine.mr.config-override.mapreduce.map.java.opts=-Xmx10g
    
kylin.engine.mr.config-override.mapreduce.map.memory.mb=10240
    
kylin.engine.mr.config-override.mapreduce.reduce.memory.mb=10240
  • Spark 任务配置重写
kylin.engine.spark-conf.spark.driver.memory=8G
    
kylin.engine.spark-conf.spark.executor.memory=12G

4. 维度转度量优化

设计的时候尽量去减少维度,去增加度量, 这时候就有个度量 EXTENDED_COLUMN

比如 city_id,city_name, 这时候就可以保留 city_id 作为维度, city_name 设置成度量 EXTENDED_COLUMN

这一步说明:

  • 这样做的好处, 大大减少了维度组合, 减少了构建时各个步骤的时间

  • 这样做的要求有 :

    1. 必须city_id,city_name 是一一对应的,一对多的时候,会随机选一个city_name

    2. city_name 不可作为 查询条件,不可以有city_name = ‘’ 或者 like “”

    3. city_name 可以作为group by 字段

    4. city_name 直接查询没有值, select distinct city_name 或者 select city_name from t group by city_name 有值

    5. 如果想直接查询某个字段,比如名称、计算结果值等, 可以使用RAW 维度

特别说明: 官方文档说是可以节省空间,但是实际构建时, 构建结果, 这种方式构建结果,比正常占用的要大, 可能与 extendedcolumn(100) 这个100有关系,未验证

不过这种构建方式速度会加快很多,是肯定的

5. 设计优化

kylin的设计优化 主要在设计, 也就是 Advanced Setting 这里的设置

(1)Aggregation Groups (聚集组)
聚集组:用来控制哪些cuboid需要计算。

适用场景:不是只需要计算base cuboid的情况下,都需要聚集组。

注意事项:一个维度可以出现在多个聚集组中,但是build期间只会计算一次。

如果不设置聚集组,默认情况下只会计算 base cuboid。

聚集组不宜太多。

这一步说明 : 聚集组是可以根据需求设置多个, 把自己常用的组合组成确定的一个聚集组, 可以大大优化查询效率

  • derived (衍生维度) 这一步是在 Dimensions 选择时设置的,放到一起说了
衍生维度:维表中可以由主键推导出值的列可以作为衍⽣维度。

使用场景:以星型模型接入时。例如用户维表可以从user_id推导出用户的姓名,年龄,性别。

优化效果:维度表的N个维度组合成的cuboid个数会从2的N次方降为2。

这一步说明: 衍生维度不参与维度组合,所以衍生维度多少并不会增加构建的复杂度和大小

  • Mandatory Dimensions (强制维度)
强制维度:所有cuboid必须包含的维度,不会计算不包含强制维度的cuboid。

适用场景:可以将确定在查询时一定会使用的维度设为强制维度。例如,时间维度。

优化效果:将一个维度设为强制维度,则cuboid个数直接减半。
  • Hierarchy Dimensions (层次维度)
层次维度:具有一定层次关系的维度。

使用场景:像年,月,日;国家,省份,城市这类具有层次关系的维度。

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

这一步说明: 简单说明下层次的意思,就是在 group by 的时候 group by city_id,district_id 和 group by district_id 是一个意思, 有层级关系,所以可以这么设置

  • Joint Dimensions (联合维度)
联合维度:将几个维度视为一个维度。

适用场景:
1 可以将确定在查询时一定会同时使用的几个维度设为一个联合维度。

2 可以将基数很小的几个维度设为一个联合维度。

3 可以将查询时很少使用的几个维度设为一个联合维度。

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

(2)Rowkeys
  • 查询频率越高的维度在Rowkey中的顺序需要越靠前。

  • Encoding 默认dict, 当维度基数特别大的时候, 会报错, 需要调整为 其他的编码方式 比如"fixed_length"、"integer"等等

  • Length fixed_length等编码方式需要设置长度, 这里需要注意的是,长度不能设置短于实际数据长度,否则会导致数据被截断

  • 当一个字段又作维度,又做度量时,也不能使用dict 需要使用其他编码方式 比如"fixed_length"、"integer"等等

  • Shard By 这个设置为 true 时, 会按照这个字段来分片, 选择高基维会加快构建, 后面会有详细说明

(3)Mandatory Cuboids (强制组合) 用的不多,再学习
(4)Cube Engine (构建引擎)

目前有 MapReduce 和 spark

关于选择之后影响哪几部分 下面会有详细说明

这里说下优劣势 :

MapReduce

  • 构建速度适用于 大批量的, 多复杂构建如 count(distinct)

  • 构建相对稳定, 不容易报错

  • 构建速度相对较慢

spark

  • 构建速度适用于 小批量的, 如 sum, max这种

  • 不太稳定, 内存不够容易报错

  • 构建速度很快

(5)Advanced Dictionaries

这一步主要失针对 count(distinct) 这种计算的

当表是 增量表时 class 选择 org.apache.kylin.dict.GlobalDictionaryBuilder

当表是 全量表是 calss 选择 org.apache.kylin.dict.global.SegmentAppendTrieDictBuilder

两者区别

  • 默认的 Global 是全局的字典, 可以保证在各个地方都能字典一致, 所以分区构建时必须采用, 否则会导致计算值不一致
    但是这个 会随着时间的增加 而增长, 会越来越大, 越来越慢

  • Segment 不是全局的, 所以不能增量构建时适用,适用于全量构建, 构建速度快, 且不会随时间而增大

(6)Advanced Snapshot Table (从未用过)

为全局 lookup 表而设计,提供不同的存储类型

(7)Advanced ColumnFamily

如果有超过一个的 COUNT DISTINCT 或 TopN 度量, 你可以将它们放在更多列簇中,以优化与HBase 的I/O。

二. 优化 Cube构建

构建各个步骤

1. Create Intermediate Flat Hive Table (创建Hive的中间平表)

这一步将数据从源Hive表提取出来(和所有join的表一起)并插入到一个中间平表。

如果Cube是分区的,Kylin会加上一个时间条件以确保只有在时间范围内的数据才会被提取。

这一步说明:

  • 从hive里面取表,所以依赖hive,且是一个MapReduce任务,更换spark引擎对此无影响,当然,这步的查询spark并无多少优势,所以不需要用spark来优化

  • 分区构建的分区字段 一定要是分区表的分区字段,特别是大表的时候,否则这步的查询将非常耗时

2. Redistribute Flat Hive Table (重新分发中间表)

在之前的一步之后,Hive在HDFS上的目录里生成了数据文件:有些是大文件,有些是小文件甚至空文件。

这种不平衡的文件分布会导致之后的MR任务出现数据倾斜的问题:有些mapper完成得很快,但其他的就很慢

这一步说明:

  • 这步是为了解决数据倾斜问题, 正常情况下是采用 DISTRIBUTE BY RAND()这种方式

  • 可以通过 “Advanced Setting” -> “Rowkeys” -> “Shard By” -> true 来指定一个列为分片字段, 可以预先分类,大大提升后续的构建速度,优化cube的存储空间

  • 指定为分片的列的特点 : 1.高基数的维度列 2.出现在很多的cuboid

3. Extract Fact Table Distinct Columns (提取事实表的唯一列)

在这一步骤Kylin运行MR任务来提取使用字典编码的维度列的唯一值。

实际上这步另外还做了一些事情:通过HyperLogLog计数器收集cube的统计数据,用于估算每个cuboid的行数

这一步说明:

  • 这一步是后续计算的基石,如果这一步很慢,就是设计过于复杂,考虑重新设计

  • 这个也对我们提出了要求,尽量减少构建的维度,可以加快构建的速度

  • 特别说明: 度量方式 EXTENDED_COLUMN, 正是通过转化维度为度量的方式, 减少了这一步,及其之后的构建时间

4. Build UHC Dictionary (构建UHC维度字典)

超高基数列(Ultra High Cardinality,UHC)

有了前一步提取的维度列唯一值,Kylin会在内存里构建字典

5. Build Dimension Dictionary (构建维度字典)

第 4, 和第5 步 说明:

  • 如果唯一值集合很大,Kylin可能会报出类似"字典不支持过高基数",“无法读取过高基字典”,“Failed to read big resource /dict/”,

    则需要更改其他的编码方式 比如"fixed_length"、"integer"等等

    或者切换构建引擎, 会更改构建的dict

    如果这两种种方式解决不了,那只能手动从后台清理出错的dict,再重新构建, 这是个bug 与 KYLIN-4153有关, 在kylin 2.6.4修复。

  • kylin会将字典所有的值都加载进内存, 导致对堆内存的消耗非常可观,所以使用字典类型就不再合适了

  • 如果一个数据集中有多个UHC,最好还使用kylin的高级特性聚合组来对维度进行分组,

    将某一个UHC和必须和这个UHC一起使用的维度分在一个聚合组中,避免两个或者多个UHC同时出现在一个分组中,导致cube膨胀

6. Save Cuboid Statistics (保存cuboid的统计数据)

7. Create HTable (创建 HTable)

第6 和 第7 步快速且轻量, 不耗时

8. Build Base Cuboid (构建基础cuboid)

这一步用Hive的中间表构建基础的cuboid,是"逐层"构建cube算法的第一轮MR计算。

Mapper的数目与第二步的reducer数目相等

9. Build N-Dimension Cuboid (构建N维cuboid)

这些步骤是"逐层"构建cube的过程,每一步以前一步的输出作为输入,然后去掉一个维度以聚合得到一个子cuboid

通常来说,从N维到(N/2)维的构建比较慢,因为这是cuboid数量爆炸性增长的阶段

过(N/2)维构建的步骤,整个构建任务会逐渐变快

10. Build Cube In-Mem (构建cube)

这个步骤使用一个新的算法来构建cube:"逐片"构建(也称为"内存"构建)。

它会使用一轮MR来计算所有的cuboids,但是比通常情况下更耗内存

说明:

  • 第8 、 第9 和第10 步 在使用 spark引擎的时候 被 “Build Cube with Spark” 所代替

  • 第10 步 可以通过 增加内存的方式来提升构建速度, 具体参数见参数优化

11. Convert Cuboid Data to HFile (将cuboid数据转换为HFile)

这一步启动一个MR任务来将cuboid文件(序列文件格式)转换为HBase的HFile格式

12. Load HFile to HBase Table (将HFile导入HBase表)

这一步使用HBase API来讲HFile导入region server,这是轻量级并快速的一步

13. Update Cube Info (更新cube信息)

在导入数据到HBase后,Kylin在元数据中将对应的cube segment标记为ready

14. Hive Cleanup (清理资源)

将中间宽表从Hive删除。这一步不会阻塞任何操作,因为在前一步segment已经被标记为ready

15. Garbage Collection on HBase (HBASE 垃圾回收)

三. 查询优化

1. 增加聚合组

当我们构建的时候, 只对某几个聚合的方式有需求,其他的聚合方式不关心的时候, 可以采用指定聚合组的方式

举例说明: 当有个 全量表 t_all (A,B,C,D)

我们只关心 A,B,C 和 A,B,D两个组合对应的结果集

select A,B,C,count(*) from t_all group by A,B,C 
和 
select A,B,D,count(*) from t_all group by A,B,D 
  • 解决方案: 这个时候我们就可以指定聚合组为这两个组合, 这样就排除了其他的组合

  • 优化结果: 构建和查询效率都大大提高

2. 增加加一个新的单一的cube

当我们构建的时候, 对一个表有多个sql需求的时候, 这个表又比较大, 有的sql又比较单一, 可以采用增加单一cube的方式

举例说明: 当有个 分区表 t_part (A,B,part) 、 分区字段 part

除了正常的

select A,count(distinct B) from t_part group by A 这种sql 

还有个特别的查询

select max(part) from t_part

这个查询对性能要求就比较高,这个分区表又比较大,这又是个跨分区的查询,因为没有聚合的特性, 所以也没有办法指定聚合组

  • 解决方案: 新建一个只有 part字段的 cube, 维度, 聚合组,绑定维度均为part 的一个cube

  • 优化结果: 这样就极大的增加了这个sql的查询效率,虽然增加了一个cube,但是这个cube 很简单, 构建也很快, 影响不大

3. 增加hbase族群

在Advanced Setting -> Advanced ColumnFamily 这步设置时

如果有超过一个的 COUNT DISTINCT 或 TopN 度量, 你可以将它们放在更多列簇中, 可以提升查询效率

4. 调整rowkey顺序

在Advanced Setting -> Rowkeys 这步设置时

可以通过按住 ID 下数字的位置 拖动字段的顺序, 来优化rowkey的设置

  • 拖动原则: 把更高基维度, 更加常用的查询维度, 拖动到最前面, 这样检索的时候效率更高

5. 新增查询sql中查询条件字段到聚合组

例如 查询sql

select count(*) from t_table 
where a = '1'
and b = '2'
and c = '99999' 

当 c 字段 不在聚合组中的时候, 查询时非常慢的,甚至于超过集群扫描条数上限

这时候只要把 c 字段 加入聚合组即可

四. 安全操作线上cube

1. 不需要更改model

这个时候只需要

clone cube -> build cube_clone -> disable 原cube -> purge 原cube -> 修改 原cube -> build 原cube -> disable cube_clone -> drop cube_clone

这个好处就是可以不更改原来的cube名称, 也就不需要更改workflow

2. 需要更改model

由于需要更改model 这个时候clone cube 就不可以了,

这时候只需要

new 新model -> new 新cube -> build 新cube -> disable 原cube -> drop 原cube

这样的方法就是需要更改原来的cube了,需要更改workflow, 当然如果不想更改, 可以采用类似1 的操作

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值