速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

Apache Kylin 权威指南(六):Cuboid 剪枝优化

  • 2020-04-18
  • 本文字数:3380 字

    阅读完需:约 11 分钟

Apache Kylin权威指南(六):Cuboid剪枝优化

编者按:本文节选自华章科技大数据技术丛书 《Apache Kylin 权威指南(第 2 版)》一书中的部分章节。

Cuboid 剪枝优化

维度的组合

由之前的章节可以知道,在没有采取任何优化措施的情况下,Kylin 会对每一种维度的组合进行聚合预计算,维度的一种排列组合的预计算结果称为一个 Cuboid。如果有 4 个维度,结合简单的数学知识可知,总共会有 24=16 种维度组合,即最终会有 24=16 个 Cuboid 需要计算,如图 1 所示。其中,最底端的包含所有维度的 Cuboid 称为 Base Cuboid,它是生成其他 Cuboid 的基础。



图 1 四维 Cube


在现实应用中,用户的维度数量一般远远大于 4 个。假设用户有 10 个维度,那么没做任何优化的 Cube 总共会存在 210=1024 个 Cuboid,而如果用户有 20 个维度,那么 Cube 中总共会存在 220=1048576 个 Cuboid!虽然每个 Cuboid 的大小存在很大差异,但是仅 Cuboid 的数量就足以让人意识到这样的 Cube 对构建引擎、存储引擎来说会形成巨大的压力。因此,在构建维度数量较多的 Cube 时,尤其要注意进行 Cube 的剪枝优化。

检查 Cuboid 数量

Apache Kylin 提供了一种简单的工具供用户检查 Cube 中哪些 Cuboid 最终被预计算了,将其称为被物化(materialized)的 Cuboid。同时,这种工具还能给出每个 Cuboid 所占空间的估计值。该工具需要在 Cube 构建任务对数据进行一定的处理之后才能估算 Cuboid 的大小,具体来说,就是在构建任务完成“Save Cuboid Statistics”这一步骤后才可以使用该工具。


由于同一个 Cube 的不同 Segment 之间仅是输入数据不同,模型信息和优化策略都是共享的,所以不同的 Segment 中被物化的 Cuboid 是相同的。因此,只要 Cube 中至少有一个 Segment 完成了“Save Cuboid Statistics”这一步骤的构建,那么就能使用如下的命令行工具去检查这个 Cube 中的 Cuboid 的物化状态:


bin/kylin.sh org.apache.kylin.engine.mr.common.CubeStatsReader CUBE_NAME
CUBE_NAME 想要查看的Cube的名称
复制代码


该命令的输出如图 2 所示。



图 2 CubeStatsReader 的输出


在该命令的输出中,会依次打印出每个 Segment 的分析结果,不同 Segment 的分析结果基本趋同。在上面的例子中 Cube 只有一个 Segment,因此只有一份分析结果。对于该结果,自上而下来看,首先能看到 Segment 的一些整体信息,如估计 Cuboid 大小的精度(hll precision)、Cuboid 的总数、Segment 的总行数估计、Segment 的大小估计等。


Segment 的大小估算是构建引擎自身用来指导后续子步骤的,如决定 mapper 和 reducer 数量、数据分片数量等的依据,虽然有的时候对 Cuboid 的大小的估计存在误差(因为存储引擎对最后的 Cube 数据进行了编码或压缩,所以无法精确预估数据大小),但是整体来说,对于不同 Cuboid 的大小估计可以给出一个比较直观的判断。由于没有编码或压缩时的不确定性因素,因此 Segment 中的行数估计会比大小估计来得更加精确一些。


在分析结果的下半部分可以看到,所有的 Cuboid 及其分析结果以树状的形式打印了出来。在这棵树中,每个节点代表一个 Cuboid,每个 Cuboid 的 ID 都由一连串 1 或 0 的数字组成,数字串的长度等于有效维度的数量,从左到右的每个数字依次代表 Cube 的 Rowkeys 设置中的各个维度。如果数字为 0,则代表这个 Cuboid 中不存在相应的维度,如果数字为 1,则代表这个 Cuboid 中存在相应的维度。


除了最顶端的 Cuboid 之外,每个 Cuboid 都有一个父 Cuboid,且都比父 Cuboid 少了一个“1”。其意义是这个 Cuboid 是由它的父节点减少一个维度聚合得来的(上卷,即 roll up 操作)。最顶端的 Cuboid 称为 Base Cuboid,它直接由源数据计算而来。Base Cuboid 中包含了所有的维度,因此它的数字串中所有的数字均为 1。


每行 Cuboid 的输出除了 0 和 1 的数字串以外,后面还有每个 Cuboid 的具体信息,包括该 Cuboid 行数的估计值、该 Cuboid 大小的估计值,以及该 Cuboid 的行数与其父节点的对比(Shrink)。所有的 Cuboid 的行数的估计值之和应该等于 Segment 的行数估计值。同理,所有的 Cuboid 的大小估计值之和等于该 Segment 的大小估计值。


每个 Cuboid 都是在它的父节点的基础上进一步聚合产生的,因此理论上来说每个 Cuboid 无论是行数还是大小都应该小于它的父 Cuboid。但是,由于这些数值都是估计值,因此偶尔能够看到有些 Cuboid 的行数反而还超过其父节点、Shrink 值大于 100%的情况。在这棵“树”中,可以观察每个节点的 Shrink 值,如果该值接近 100%,说明这个 Cuboid 虽然比它的父 Cuboid 少了一个维度,但是并没有比它的父 Cuboid 少很多行数据。换言之,即使没有这个 Cuboid,在查询时使用它的父 Cuboid,也不会花费太大的代价。


关于这方面的详细内容将在后续 3.1.4 节中详细展开。

检查 Cube 大小

还有一种更为简单的方法可以帮助我们判断 Cube 是否已经足够优化。在 Web GUI 的“Model”页面中选择一个 READY 状态的 Cube,当把光标移到该 Cube 的“Cube Size”列时,Web GUI 会提示 Cube 的源数据大小,以及当前 Cube 的大小与源数据大小的比例,称之为膨胀率(Expansion Rate),如图 3 所示。



图 3 查看 Cube 的膨胀率


一般来说,Cube 的膨胀率应该为 0%~1000%,如果一个 Cube 的膨胀率超过 1000%,Cube 管理员应当开始挖掘其中的原因。通常,膨胀率高有以下几个方面的原因:


  • Cube 中的维度数量较多,且没有进行很好的 Cuboid 剪枝优化,导致 Cuboid 数量极多;

  • Cube 中存在较高基数的维度,导致包含这类维度的每一个 Cuboid 占用的空间都很大,这些 Cuboid 累积造成整体 Cube 体积过大;

  • 存在比较占用空间的度量,如 Count Distinct 这样的度量需要在 Cuboid 的每一行中都保存一个较大的寄存器,最坏的情况会导致 Cuboid 中每一行都有数十千字节,从而造成整个 Cube 的体积过大;


……


因此,遇到 Cube 的膨胀率居高不下的情况,管理员需要结合实际数据进行分析,可灵活地运用本章接下来介绍的优化方法对 Cube 进行优化。

空间与时间的平衡

理论上所有能用 Cuboid 处理的查询请求,都可以使用 Base Cuboid 来处理,就好像所有能用 Base Cuboid 处理的查询请求都能够通过直接读取源数据的方式来处理一样。但是 Kylin 之所以在 Cube 中物化这么多的 Cuboid,就是因为不同的 Cuboid 有各自擅长的查询场景。


面对一个特定的查询,使用精确匹配的 Cuboid 就好像是走了一条捷径,能帮助 Kylin 最快地返回查询结果,因为这个精确匹配的 Cuboid 已经为此查询做了最大程度的预先聚合,查询引擎只需要做很少的运行时聚合就能返回结果。每个 Cuboid 在技术上代表着一种维度的排列组合,在业务上代表着一种查询的样式;为每种查询样式都做好精确匹配是理想状态,但那会导致很高的膨胀率,进而导致很长的构建时间。所以在实际的 Cube 设计中,我们会考虑牺牲一部分查询样式的精确匹配,让它们使用不是完全精确匹配的 Cuboid,在查询进行时再进行后聚合。这个不精确匹配的 Cuboid 可能是 3.1.2 节中提到的 Cuboid 的父 Cuboid,甚至如果它的父 Cuboid 也没有被物化,Kylin 可能会一路追溯到使用 Base Cuboid 来回答查询请求。


使用不精确匹配的 Cuboid 比起使用精确匹配的 Cuboid 需要做更多查询时的后聚合计算,但是如果 Cube 优化得当,查询时的后聚合计算的开销也没有想象中的那么恐怖。以 3.1.2 节中 Shrink 值接近 100%的 Cuboid 为例,假设排除了这样的 Cuboid,那么只要它的父 Cuboid 被物化,从它的父 Cuboid 进行后聚合的开销也不大,因为父 Cuboid 没有比它多太多行的记录。


从这个角度来说,Kylin 的核心优势在于使用额外的空间存储预计算的结果,来换取查询时间的缩减。而 Cube 的剪枝优化,则是一种试图减少额外空间的方法,使用这种方法的前提是不会明显影响查询时间的缩减。在做剪枝优化的时候,需要选择跳过那些“多余”的 Cuboid:有的 Cuboid 因为查询样式永远不会被查询到,所以显得多余;有的 Cuboid 的能力和其他 Cuboid 接近,因此显得多余。但是 Cube 管理员不是上帝,无法提前甄别每一个 Cuboid 是否多余,因此 Kylin 提供了一系列简单工具来帮助完成 Cube 的剪枝优化。


图书简介https://item.jd.com/12566389.html



相关阅读


Apache Kylin权威指南(一):背景历史和使命


Apache Kylin权威指南(二):工作原理


Apache Kylin权威指南(三):技术架构


Apache Kylin权威指南(四):核心概念


Apache Kylin权威指南(五):Getting Started


2020-04-18 10:001507

评论

发布
暂无评论
发现更多内容

高性能 JavaScriptの七 -- 编程实践小技巧

空城机

JavaScript 大前端 6月日更

【Vue2.x 源码学习】第八篇 - 数组的深层劫持

Brave

源码 vue2 6月日更

证券互动问答平台关键词监控提醒

木头

互动平台 证券监控 股市消息 监控提醒

春色满园关不住,带你体验阿里云 Knative

阿里巴巴云原生

云原生

缓存穿透、缓存雪崩、缓存击穿问题与优化方案

Skysper

SpringBootApplication注解

梦倚栏杆

Packer 自动化镜像 Windows 安装过程

HoneyMoose

读深入ES6记[二]

蛋先生DX

ES6 6月日更

Locust完成gRPC协议的性能测试

陈磊@Criss

Java 并发编程——线程池开篇

Antway

6月日更

【LeetCode】从上到下打印二叉树 Java题解

Albert

算法 LeetCode 6月日更

【Flutter 专题】109 图解自定义 ACERadio 单选框

阿策小和尚

Flutter 小菜 0 基础学习 Flutter Android 小菜鸟 6月日更

内嵌双向链表的设计与实现

实力程序员

模块六作业

c

架构实战营

如何进行可视化大屏视觉设计?

博文视点Broadview

待办事项列表,敏捷项目管理的核心工件

万事ONES

Scrum 敏捷 研发管理 ONES

【布道API】浅谈API设计风格

devpoint

Rest API 6月日更

Mybatis 二级缓存简单示例

Java mybatis

一文教会你认识Vuex状态机

华为云开发者联盟

Vue 应用 vuex 事件 父子组件

那个陪我打王者的兄弟进了阿里

艾小仙

Kubernetes 的自动伸缩你用对了吗?

张晓辉

Kubernetes k8s最佳实践

聊聊追求测试技术导致过度测试

陈磊@Criss

☕️【Java技术之旅】站在Linux操作系统角度去看Thread(线程)

洛神灬殇

线程 Thread 6月日更 内核线程

【21-1】21 连更第一篇

耳东@Erdong

6月日更

你愿意被管理么?

escray

学习 极客时间 朱赟的技术管理课 6月日更

这些书都学完,绝对是编程界的大佬

看山

Java 程序员 6月日更

当人工智能遇上视频直播——基于Agora Web SDK实现目标识别

dajyaretakuya

深度学习 音视频 WebRTC 声网 TensorFlow.js

递归全排列问题(两种方法 Java实现)

若尘

数据结构 递归 6月日更

密码学系列之:生日攻击

程序那些事

加密解密 密码学 程序那些事

Python——字符串转换与处理

在即

6月日更

Webpack 系列:如何编写loader

范文杰

webpack 6月日更

Apache Kylin权威指南(六):Cuboid剪枝优化_架构_Apache Kylin核心团队_InfoQ精选文章