一、数据治理体系
和行业大部分公司一样,快手数据治理也是分为四大部分:成本、质量、效率和安全。
1. 效率
分为数据开发效率和数据消费效率。开发效率主要关注模型开发效率,消费效率主要关注模型是否足够易用,查询响应是否足够快。
2. 安全
同样也是分为生产阶段的安全、还有消费阶段的安全。
3. 质量
分为避免发生、主动发现、故障结果、故障复盘。
- 避免发生:在设计、开发、测试和验收环节,是否符合规范。
- 主动发现:出了问题之后,一定是我们自己先发现,而不是用户告诉我们。首先要做到监控覆盖全面,其次还要实现有效的告警,确认告警的有效率,是非常关键、也是最难的部分。
- 故障结果:各个级别故障数量,是否在预期范围内,我们对故障是容忍的,只要控制在一定数量内。
- 故障复盘:首先,复盘要足够深刻,抓住问题本质、找到共性。其次,复盘会产生很多待改进问题,这些问题是否被及时解决了。
4. 成本
数据成本由三部分组成:存储成本、计算成本、流量成本。
- 存储成本:存储效率是白盒化的重点:压缩比、压缩性能、副本数。压缩比关注存储密度,压缩性能关注压缩耗时,副本数关注能否用较少的副本存储数据,确保数据不丢失。
- 计算成本:CPU 平均利用率衡量资源调度能力。如果调度能力不行,很容易出现负载不均,可能部分机器打满,另一部分却很空闲,导致集群整体利用率上不去。单 CU 处理数据量衡量引擎算力水平,随着我们不断去优化计算引擎,单 CU 处理的数据量也会随之升高。
- 流量成本
本次分享主要是讲成本部分,我们通过三个白盒化实现:引擎白盒化 + 数仓白盒化 + 工具白盒化。
二、引擎白盒化
“引擎白盒化”,是一个具体的项目代号,这个项目包含很多优化点:HBO 自动化调参、压缩算法替换、引擎算子分析……
1. HBO 自动调参
什么是 HBO 自动调参?可以想象一下,在没有 HBO 之前,调参主要通过人工完成的,但有三大弊端:
难度高:对于一个普通数据开发者来说,这无疑是一件很难做好的事情。首先需要理解引擎原理,分析作业的几十个指标,找到性能问题,再调参,还不一定能调好。
易失效:随着时间推移,计算逻辑、数据内容可能早已发生改变,之前的调参过一段时间就失效了。
成本大:可以想像一下,对几万、十万个作业进行人工调参,基本是不可能完成的事情。
HBO 的出现就可以很好地解决这三大问题,它是通过分析任务运行历史,自动优化作业执行参数,从而使作业一直处于贴近最优的状态。
HBO 为什么可以提升性能、降低成本?主要通过三个方面来实现的:
(1)合理资源配额:通过识别作业对 CPU 和内存的需求,自适应扩缩容,防止错配。
(2)优化任务分片:通过分析任务时长,灵活调整分片参数,以防止过短或过长。
(3)任务优化功能参数:通过调整小文件合并、压缩算法、Broadcast 等参数,以提升性能。
HBO 调参过程就是数据驱动的过程:
第1步,构建画像,需要采集几十个决策指标,指标的质量直接决定了 HBO 效果,需要的是严谨。
第2步,参数粗调,通过数据指标+预设规则,进行首次粗调参,粗调时会尽量的保守,比如:CPU/内存会尽可能多预留一些,以防止运行变慢。
第3步,参数下发,将调参的结果下发给各个任务,下发之后,下一个周期就会产生影响。
第4步,参数精调,粗调之后,我们能感知调参效果,根据最新反馈进一步精调、再精调。
以上列举了部分调参列表,供参考。接下来我们看看第二个优化项,存储压缩算法替换。
2. 压缩算法替换
先看看快手大数据存储现状:
- 技术选型:湖仓的底层存储是 PARQUET + GZIP。
- 数据规模:新增 PB / 存量 EB。
- 数据读写:读写比大于 20:1,读取数据量远高于写入数据量,所以我们会更加关注解压性能,而压缩性能稍微差点,是可以接受的。
- 存储周期:很多数据存储周期比较长,甚至是永久存储,满足审计相关的需求,所以对于存储系统来说,压缩比永远是我们首要考虑的。
在压缩算法这块,快手选型是 GZIP,纵观行业里的很多公司,一开始也都是 gzip,不过这些公司都逐步切换到了 zstd,因此为他们节省了大量的成本。以上图片列举了:亚马逊、推特、优步几家公司在推特上的一些讨论。
Zstd 既有着和 zlib 相当的压缩率,同时还有媲美 snappy 的压缩性能,可谓是集合了两者的优势。
另外,我们也在线上进行了实测,压缩率提升 3%~12%,压缩级别越高,压缩率也就越高。Zstd 压缩级别分为 1-22,根据我们的观察,压缩级别超过 12 之后,压缩率提升的就不是很明显了,所以我们最终选择的压缩级别,最高不超过 12。
此外,我们还做了很多其它的测试,稳定性、准确性、兼容性,这三方面都没有问题。提示一下:zstd-jni 1.5.0 以下的版本有不少的 bug,建议升级到 1.5 以上,完全向下兼容。
3. 引擎算子分析
什么是引擎算子分析?通过对 Spark 引擎进行多视角、多维度深度剖析,提升对引擎的认知,使算力可解释、明确算力瓶颈、挖掘算力潜在优化点。
通过不同的视角进行深度剖析,在不同的视角下,能发现不一样的问题,找到不同的潜在优化点。本次介绍最重要的 3 个分析视角,分别是执行过程视角、物理算子视角、UDF 函数视角。
(1)执行过程视角:将计算划分为十大过程,分析这十大过程的构成、资源开销、时间开销。
(2)物理算子视角:物理算子和逻辑算子有着对应关系,一个逻辑算子有多种不同物理实现,在不同的场景下,匹配不同物理算子。物理算子内部实现是否高效,以及是否被正确使用,都对性能有重大影响。
(3)UDF 函数视角:对几百个 UDF 函数使用情况进行分析,既包含引擎内置的,也包含用户实现的。
引擎理解同样也是数据驱动的,拆解引擎需要用到四大数据源:
(1)QueryPlan:通过解析 SQL,生成执行计划,从而对算子进行分析。
(2)StackTrace:用于分析调用堆栈,生成直方图、火焰图,进行性能分析。
(3)EventLog:作业在运行过程中生成的 DAG 图谱以及各种指标,这对引擎理解也有极大的帮助。
(4)GcLog:最后是 GcLog,主要用来精准分析内存用量,减少 OOM 发生的概率。
接下来看一组数据,以上是对执行过程的分析,我们看看耗时最大的四个过程,有了数据之后,只需要通过进一步下钻分析,就能找到优化方向:
数据扫描:占比最大,超过 30%,有点超出预期,说明潜在优化空间是非常大的。
数据交换:占比第二,大概 20%,也是非常高的。
数据聚合:占比第三,大概 15%。
UDF 调用:占比第四,大概 14%。
Aggregate:整体比较健康。
Join:SortMergeJoinExec 性能差,但占比 95.3%,可通过 HBO 调参转换BroadcastHashJoinExec。
Scan:数据读取和压缩占比低,而数据处理占比 75.94%,后续主要考虑如何提升数据处理的效率(包含 schem 处理,行列转换等)。
首先,Json 处理占了 1/3 还要多,这是非常不健康的,这是重点要去解决的。第一,要优化 JSON 处理性能,第二,我们也得看看为什么有这么多的 JSON 要处理?肯定有哪里不对劲。
其次,平台公共 UDF,整体看起来还算可以。
最后,业务 UDF,可以看到占比也很高,也是需要重点去解决的。
当我们有了数据、看清了现状,接下来应该如何应对呢?
(1)我们需要去做分析,判断。判断是否合理?是否有优化空间?空间有多大?
(2)需要确保使用方法正确。如果有一辆好赛车,但是不懂驾驶,就很难发挥出应有的性能。
(3)可以看看某些零部件,是否有替代方案。例如 JSON 处理,压缩算法。
(4)如果以上都解决不了,那就只能自己动手去重构某些零部件了,尤其是一些关键零部件。
更多的优化,也还在进行当中。
三、数仓白盒化
1. 数仓架构度量
讲数仓白盒化之前,我们必须先要弄清楚,好的数仓标准是什么样的?只有搞清楚了数仓架构的量化指标,才能指导我们进行优化:
完整度:衡量数据模型建设,是不是足够完整、通用,满足的需求是不是足够广泛。其实就是分析跨层引用多不多?跨层引用过多,数仓模型一定不是完整的。
复用度:主要看三个指标(引用系数、重复计算、链路深度)。
规范性:这是基础要求,但做好并不容易,尤其是一开始没做好,后续想要捡起来,会非常困难。
2. 如何减少重复计算
先来看一个相似算子的示例。图中给出了三个查询,拆解为语法树之后,不难发现,这其中隐藏了非常多的相似代码片段。图上用了四种不同颜色,进行标识,相同的颜色代表此部分计算逻辑是相似的,这些相似片段,通常都会带来数倍资源开销。
如何识别相似的代码片段呢?
第1步,获取任务列表,我们要获取到所有执行任务的 SQL。
第2步,生成执行计划。
第3步,逐层算子签名,通过后序遍历 AST,逐层向上展开,直到完成整棵数的签名,得到签名集合。
第4步,重复算子识别,通过签名碰撞,去发现重复的算子。
第5步,计算算子代价,代价越高的重复算子,就越应该抽象、沉淀下来。
第6步,合并重复算子,提升处理速度,降低成本。
这是我们内部的一组数据,直观的感觉就是重复比例有点大。可以看到聚合的相似算子达到了惊人的 43%,这是非常之高的,连接算子也达到了 23%,最后,还有 4.5% 的 INSERT 算子是相似的,INSERT 是最后输出的模型,这说明了有 4.5% 的模型是相似的。如果说,我们不去自动干预、自动治理,只会越来越恶化。
当我们识别了相似算子,看清了现状,那么到底有什么价值呢?主要有三个:
指导治理:向用户展示当前现状,并给出整改优化建议.
辅助开发:主要包含模型选择和优化。
查询加速:通过自动物化视图,实现查询加速,这个过程对用户是完全透明的。
3. 如何降低链路层级
想要降低链路层级,我们得先了解现状。上图是线上某条生产链路,展示的是任务依赖关系。可以获得一些信息:首先是,链路层级非常深:达到了惊人的 39 层。其次是,跨层依赖非常多:可以明显看出来很多末尾的 ADS 节点,甚至会依赖最源头ODS。这也间接导致了很多问题,例如:数据及时性差、生产成本高、数据质量难以控制。
为什么会出现这种现象?既要满足快速变化的业务需求,又要不断地抽象和沉淀,还要确保不出现质量问题。既要又要还要,通常来说,这三者很难同时兼顾,所以随着时间变化,导致数仓劣化。
那么怎么去改变现状呢?第一种解决方案是机器辅助治理。首先,通过构建算子级血缘,还原数据加工逻辑。其次,分析并发现模型中的不合理,生成整改优化建议。最后,由用户根据建议去优化。
第一种解决方案,只是用于短期治理,并没有从根本解决问题。那么终极方案是什么呢?我认为是逻辑层和物理层解耦,将逻辑模型和物理模型完全分离开来。
逻辑层:数据开发者只需要根据业务需求去设计逻辑模型,而完全不用理会底层实现。
物理层:物理模型和链路,完全由机器自动化构建,并且根据逻辑模型的变化,不断的迭代和调整,使数仓一直处于贴近最优的状态。
4. 常规治理自动化
首先我们不得不聊一下现状:治理属于一种事后清理工作,善后工作,大家普遍不会太重视,或者说优先级低,没时间处理。做数据治理平台的同学无一例外,都遇到了这个问题,就是推不动业务去治理。
那到底应该怎么办呢?我们的答案是:常规治理自动化!但是自动化治理,面临的最大挑战就是怎么确保自动的过程中可靠安全?万一删错了数据怎么办?万一导致了故障怎么办?其实并没那么可怕,再难的事情,只要有体系化的保障,就不用害怕,我们给出的方案是五步法自动治理:
制定标准:自动化治理一定要有严格的标准,只有形成了标准,才有可能自动化,这是先决条件。
问题识别:根据标准、根据规则去识别生产待治理的问题。
质量检验:第一道防火墙,通过波动检测、交叉验证等方式阻断部分问题发生。
治理预告:第二道防火墙,对于非常 critical 的治理动作,通过治理预告,让用户帮助阻拦问题。
快速回滚:第三道防火墙,即使出现了问题,也能快速回滚。所有的治理动作,必须是可以回滚的,否则就不能纳入到自动化范围。
四、收益分析
数据存储压缩率:提升 5%,计算资源收益:提升 16%,作业运行时长:和计算资源收益相当,缩短 14%。此外,作业失败率、GC 时间、OOM 等也有不同程度的下降
五、未来规划
未来工作更多的是延续,因为很多工作才刚开始起步,还比较浅。总的来说,有这么几件事:
数据压缩:之前主要是替换压缩算法,这个红利已经吃完了。接下来还要再继续提升的话,就需要在动态压缩、编码等方面下功夫。
数仓架构:前面我们讲的数仓白盒化,很多工作都才刚开始,特别是在模型设计、模型生产等方面。
深度范围:进一步提升引擎白盒化治理深度。
下代技术:要想在效率和成本上产生突破,必须要布局下一代技术,跳出我们现有的认知,在技术范式上取得进展。