Apache Kylin 性能优化

2,275 阅读6分钟

聚合组 Aggregation Groups

Cube Designer的Advanced Setting中可以配置Aggregation Groups。 理论上N维度Cube会构建2^N个Cuboid,随着维度的增多,Cuboid数量会指数增长,存储空间占用增大,构建时间增长。聚合组的目的是对维度分组,减少不必要的维度组合,从而减少Cuboid的数量。

4维度Cube构建16个Cuboid

分组之后的维度集合支持必要维度,层级维度,联合维度3种配置。

必要维度 Mandatory Dimension

用户对一个或几个维度特别敏感,所有的查询中都存在GROUP BY这些维度,则可以把这些维度配置为必要维度。

A为必要维度

层级维度 Hierarchy Dimensions

具有层级关系的维度,例如国家country,省份province,城市city。这三个维度的查询有以下三类:

  1. group by country
  2. where country='xxx' group by province
  3. where country='xxx' and province='zzz' group by city

A,B,C为层级维度

联合维度 Joint Dimensions

联合维度表示不可分割的维度集合。

A,B,C为联合维度

Rowkeys 优化

Kylin使用HBase作为Cube的存储引擎。HBase是Hadoop生态的Key-Value数据库,支持按Key随机查询和写入,这个Key在HBase中叫做Rowkey。

维度编码 Encoding

对维度编码,可以将维度值转化为长度一致的字节,合适的编码方式能够有效减少对空间的占用,加速查询效率。

  1. dict 字典编码,将提取纬度的唯一值在内存中构造字典,Rowkey使用字典的key替换纬度值。 字典常驻内存,对查询影响很小,但是字典不能超过缓存大小,否则在Cube build过程中会发生缓存溢出。默认dict编码字段基数在5,000,000以内,可以通过kylin.dictionary.max.cardinality修改,这种情况更好的做法是使用其他编码类型。 dict字典适用中低基维度,如地区信息,国家,省份,城市。

  2. fixed_length 使用固定长度N存储维度值,超过长度部分会截断。如果N很大,会造成Rowkey过长,HBase性能下降。 fixed_length适用超高基场景,如电话号码,IP等。

  3. fixed_length_hex 针对值为十六进制的维度,比如Base64之后的值。

  4. integer 整数类型,不做编码转换。Length长度范围为1~8,支持的整数范围[-2^(8*N-1) ~ 2^(8*N-1)]

  5. date 日期类型。适用3个字节编码。

  6. time 时间戳类型,毫秒部分会忽略。

  7. boolean 适用1个字节表示布尔值。

维度顺序

HBase中的数据按照Rowkey的字典排序顺序存储。

  1. 查询频率高的维度放在低频率之前
  2. 高基维度放在低基维度之前

分片维度 Shard By

Rowkeys可以为一个(最多也只有一个)维度配置Shard By值为true。 Cube在构建过程中需要 将中间表的数据分布到HDFS节点,默认partition方式是随机。如果指定分片维度,则使用分片维度做partition。partition的目的是防止中间表大小差异造成数据倾斜,因此Shard By维度应该使用高基维度,以使分片粒度足够小,能够加速之后的MR计算任务。

注意分片维度和Cube的Partition维度不同,前者作用于构建阶段,后者是用于分割时间段以支持增量构建

字典优化 Advanced Dictionaries

  1. Global Dictionary 用于精确计算 COUNT DISTINCT 的字典, 它会将一个非 integer的值转成 integer,以便于 bitmap 进行去重。如果你要计算 COUNT DISTINCT 的列本身已经是 integer 类型,那么不需要定义 Global Dictionary。 Global Dictionary 会被所有 segment 共享,因此支持在跨 segments 之间做上卷去重操作。请注意,Global Dictionary 随着数据的加载,可能会不断变大。
  2. Segment Dictionary 用于精确计算 COUNT DISTINCT 的字典,与 Global Dictionary 不同的是,它是基于一个 segment 的值构建的,因此不支持跨 segments 的汇总计算。如果你的 cube 不是分区的或者能保证你的所有 SQL 按照 partition_column 进行 group by, 那么你应该使用 “Segment Dictionary” 而不是 “Global Dictionary”,这样可以避免单个字典过大的问题。
  3. 字典复用 Reuse 如果A列是是B列的子集,则A列可以复用B列的字典。

列簇优化 Advanced ColumnFamily

Kylin默认将所有度量放在HBase的1个列簇中。 每个COUNT DISTINCT度量使用bitmap存储明细数据,这样的度量基数一般都很大。多个这样的度量存储在同1个列簇计算量很大,可以将超过一个的COUNT DISTINCT 或 TopN 度量, 放在更多列簇中(不同的列簇保存在不同的Store中),以优化与HBase 的I/O。 注意,HBase的列簇最好控制在2-3个

Normal 普通列 VS Derived 派生列

在LookupTable可以设置列为Normal或者Derived。 当一个或多个维度能够从另一个维度(一般为事实表的外键)推导出来,可以考虑将这个列设置为派生列。派生列不会参与Cube计算。 New ModelNew Join Condition,左边为事实表的外键,右边为lookup表的主键

精确去重 Distinct Count

为了支持任意粒度的上卷聚合,精确去重需要保存明细数据,所以使用Bitmap保存精确去重度量数据。Kylin使用RoaringBitmap库。 RoaringBitmap只支持Int类型的数据,所以Kylin引入了全局字典,以保证在不同Segment上,String到Int的映射一致。全局字典的核心数据结构是AppendTrieDictionary。

近似去重

Kylin的近似去重,基于HLL(HyperLogLog)实现。 简单来说,每个需要被计数的值都会经过特定Hash函数的计算,将得到的哈希值放入到byte数组中,最后根据特定算法对byte数据的内容进行统计,就可以得到近似的去重结果。

膨胀率 Expansion Rate

膨胀率是指 Cube大小/原始Table大小。膨胀率应该控制在10倍以内。 影响Cube大小的因素:

  1. Cube维度数量很多,没有进行Cuboid剪枝优化,导致Cuboid数量巨大
  2. Cube中存在高基维度,造成包含这类维度的Cube很大
  3. 存在占用空间很大的度量,比如COUNT DISTINCT

超高基维度 UHC

UHC 代表 Ultra High Cardinality,即超高基数。基数表示维度不同值的数量。通常,维度的基数从数十到数百万。如果超过百万,我们将其称为超高基维度,例如:用户 ID,电话号码等。

参考文档

诸葛葛说