简介: 本文由 T3 出行大数据平台负责人杨华和资深大数据平台开发工程师王祥虎介绍 Flink、Kylin 和 Hudi 湖仓一体的大数据生态体系以及在 T3 的相关应用场景。
本文由 T3 出行大数据平台负责人杨华和资深大数据平台开发工程师王祥虎介绍 Flink、Kylin 和 Hudi 湖仓一体的大数据生态体系以及在 T3 的相关应用场景,内容包括:
- 湖仓一体的架构
- Flink/Hudi/Kylin 介绍与融合
- T3 出行结合湖仓一体的实践
这个分享有三个部分,首先探讨湖仓一体的架构,然后交流如何融合三个框架以及 T3 如何实践湖仓一体这个架构。
既然聊湖仓一体,我们先了解一下什么是湖,什么是仓。数据湖是一个很老的概念,在近些年又被热炒。业界对于数据湖到现在也没有一个统一的定义。AWS 是最早在云上推出数据湖解决方案的云服务提供商,在这里我们便引用 AWS 对数据湖的定义:“数据湖是一个集中式的存储库,允许存储任意结构的数据并且能将它应用于大数据处理,以及进行实时分析和机器学习等相关的应用场景。” 同样我们也借助于 AWS 对数据仓库做这样的定义:“数据仓库是信息的一个中央存储库。” 这里的信息是可对其进行分析,并且可做出更明智的决策。
这个定义还有详细的展开。AWS 这张图通过展示了从湖到仓的数据流向的关系,来演示数据湖与数据仓库之间的区别和联系。首先数据最初是存在于数据湖或是数据库中,然后经过数据筛选和准备之后,就会流向数据仓库来进行一些高价值的分析。这个对比表格很直观的从数据、Schema、性价比、数据质量、用户和分析这 6 个维度给出数据湖和仓的对比。
今年我们听说阿里巴巴提及的“湖仓一体”的概念。不知道大家有没有想过湖仓一体在业界是否有成功的先例?我个人认为是有的。今年 (2020年)9 月份,一家叫 Snowflake 的公司在纽交所上市。Snowflake 是一家做云数仓的公司,基于云厂商提供的基础设施提供 SaaS 平台,面向中小企业提供数据的托管和分析服务。Snowflake 自称自己是一家云数仓公司,并且在 16 年的数据顶会上发表了一篇论文来介绍他们弹性数仓的架构以及一些技术的细节。
Snowflake 其实是基于云上的对象存储,一份存储多份计算,并且计算与存储分离的这样一套架构。其实这就是 AWS 以及现在主流云厂商所主推的这个数据湖的架构。Snowflake上市的首日,他的市值就飙升到了 700 亿美元的规模。所以我个人认为 Snowflake 可以算是实行湖仓一体的一个最成功的先例。大家可以去了解一下刚谈到的这篇论文。我摘出了这 5 个点来和大家做简单的分享:
这虽然是 16 年的一篇论文,但里面的观念并不算陈旧并且仍然值得我们去学习。后续我们会简单介绍几个被我们吸收并且将会去实践的一些点,而且这些点也是 T3 出行在实现湖仓一体上很关键的地方。
首先,作为一个被很多传统的数仓广泛应用的一个架构,Shared-Nothing 还是有一些架构上的优势:
这套架构其实也有一些不足的地方:
基于这些问题,Snowflake 提出了一个叫做 Multi-Cluster Shared-Data 架构。这里我们对官方的图做了一个简单的微调。
接下来我们以分层的形式来 review 这个架构。从整体上来看,它的结构大致分为三个层次。
接下来一个点是这个架构的高可用。这里可以简单分解为 2 个方面。第一个是失败容错,第二个是在线升级。
首先,作为一个 SaaS 化的应用,它的容错性是需要体现在整体架构上。这里我们同样分层来回顾一下。
接下来我的同事(王祥虎)会跟大家介绍这 3 个框架以及它们是如何融合并最终支撑 T3 湖仓一体的实践。在介绍第二个议题前他会先介绍我们的主框架,Hudi 和 Kylin 框架,然后再介绍他们三者之间是如何两两融合。最后再介绍T3是如何构建湖仓一体的。
首先来了解一下 Hudi 是什么。Hudi 最初是由 Uber 的工程师为了满足他们的数据分析需求设计开发的一个数据湖框架。它于 2019 年 1 月份加入到 Apache 孵化器,并于 2020 年 5 月顺利毕业,成为 Apache 的顶级项目。Hudi 的名字来源于 Hadoop Upserts Deletes and Incrementals 的缩写。也就是说,Hudi 是一个支持插入、更新、删除、以及增量处理的数据湖框架。除此之外,它还支持事务性 ACID 增量处理、存储管理和时间管理。Hudi 能够管理云上超大规模上百 PB 的分析型数据集,对于所有的云服务都开箱即用,非常的方便,而且已经在 Uber 内部稳定运行了接近 4 年。
下图是 Hudi 的插件化架构。我们可以看到,Hudi 在存储、数据处理引擎、表类型、索引类型、查询视图和查询引擎方面都有比较宽松的支持。也就是说,他不与某一个组件绑定。
Hudi 支持 3 种查询,读优化查询,增量查询和快照查询。而在查询引擎方面,有 Spark 、Presto、Hive 和 Impala,实际上一些其他的组件已经支持了。
下面详细的介绍一下存储模式和视图。
在出行业务中,订单会有支付长尾的属性。也就是说一个订单开始之后,它的支付环节可能会拖的比较久。换言之,它可能会在这个用户下一次出行前才进行支付(也或许会更久,甚至永远不支付)。这种长尾属性将会导致一个超长的业务闭环窗口,会导致我们无法准确预测数据的更新时机。如果存在多级更新的话,链路会比较长,更新成本也非常的高。
下图是我们的长尾更新引发的冷数据频繁更新示意图。左侧是业务库,右侧是有依赖关系的 3 张示意表。当业务库有数据更新时,右侧需要更新的数据可能已经归档到性能相对较差的设备上,增加数据更新成本。而且如果这次数据更新会引发长链路级联更新的话,这种慢速的 I/O 还会被进一步放大。
数据的可靠性也是数据 ETL 中不可避免的问题。可能由于机器故障或者计算逻辑导致加工处理的数据失真或者完全不对,就会给运营的决策造成很大的影响。数字延迟性方面,在基于 Hive 构件的传统架构中,由于 Hive 缺少索引机制,所以数据更新大都会导致数据分区重写,且没有办法原地删除。其次小文件问题会增加 NameNode 存储和查询的负担,拖慢进程,在一定程度上增加数据延迟性。
我们再来介绍一下这个 Kylin 框架。相比较 Hudi,大家应该会对 Kylin 相对熟悉一些,它是一个开源的分布式分析型数据仓库,能够提供 Hadoop/Spark SQL 之上的数据查询窗口。最初是由 eBay 开放并贡献到开源社区,能够在亚秒内查询巨大的表。它的秘诀其实就是做预计算,针对一个星型拓扑结构数据立方体,预算多个维度组合的度量把结果写出到输出表,对外暴露查询接口实现实时查询,也就是用空间来换取存取时间。
Kylin 在今年的 9 月份发布了 4.0 alpha 版本,这是在 Kylin3 之后一个重大架构升级。使用 Parquet 代替 Hbase 存储,从而提升了文件的扫描性能,也减轻甚至消除了 Hbase 的维护负担。Kylin4 重新实现 Spark 构建引擎和查询引擎,使得计算和存储分离,也更加适用云原生的技术趋势。
伴随 Kylin3.1 发布,Kylin 与 Flink 就融合已经完成。这个特性是在 2019 年完成的,Kylin 与 Flink 的集成开始于去年 1 月,通过 Flink Batch 实现。关于 Hudi 融合,可以说 Kylin 和 Hudi 天生就是兼容的,因为 Hudi 可以将自己暴露成一张 Hive 表,用户可以像读取 Hive 一样使用 Hudi 的数据,这样对Kylin会非常友好。因为 Kylin 可以把 Hudi 当成一张 Hive 表无缝使用数据。Hudi 和 Flink 融合这个特性是我今年对社区的主要贡献。这个两张截图对应 Hudi 和 Flink 融合路上的2个里程碑式的PR。
下面来详细介绍下 Hudi 和 Flink 融合过程。Hudi 原本只支持 Spark 引擎,所以第一步是将 Hudi 与 Spark 解耦之后再去集成我们想要的引擎。
解耦的难点在于 Hudi 最初没有考虑多引擎的支持,所以从数据源读取数据到最终将数据写出到 Hudi 表,RDD 无处不在。连普通的工具类都会使用 RDD 作为基本的操作单元。与 Spark 解耦,我们评估到他的改动非常的大。其次是 Flink 与 Spark 核心抽象上的差异。Spark 认为数据是有限的数据集,而 Flink 认为数据是无界的,是一种数据流。这种抽象上的差异导致我们很难统一出一个通用的抽象。
这次改动对于 Hudi 来说是伤筋动骨的,因此我们决定要优先保证原版 Hudi 的功能和性能,当然也牺牲了部分 Flink Stream API。让 Flink 来操作 list,而用Spark 操作 RDD。这样就可以抽取一个泛型出来形成一个统一的抽象层。
抽象原则:
下面说 Flink Client DAG,这里主要分了 5 部分,
下面是 Flink 更新的代码示例。左侧是原版里面 HoodieWriteClient 简化的版本,
可以看到 insert 函数的入参是 RDD,返回值也是 RDD。右侧抽象之后的 abstract 可以看到它的入参变成了泛型I,返回值变成了 O,有兴趣的话大家可以去了解一下。
下面是我们对 Flink 如何融合的另外一个想法,就是希望做出一个 streaming source,使用 Flink 构建一个完整的从 Hudi 表读数据,再写出到 Hudi 表的 ETL 管道。
然后是我们初步的设想。左侧灰色的图里面有 5 列的 Hudi 元数据。最左侧是 hoodie_commit_time 事务列表。每一个 hoodie_commit_time 对应一个事务,每一个事务对应一批的数据。每一批数据中的每一条记录都会有一个提交的序列号,就是第 2 列 hoodie_commit_seqno 序列号。hoodie_commit_time 和 hoodie_commit_seqno 这种映射关系跟 Kafka 中的分区和 offset 的这种映射关系非常类似。后期我们可能会基于这种特点实现一个 Hoodie Streaming Source。
基于这 3 个框架之间的融合关系,我们发现分别用于计算、分析、存储的这 3 个引擎之间是相互兼容的。并且他们能够支持湖仓一体,向云原生体系靠拢。
最后我们来看一看 T3 出行是如何构建湖仓一体的。这是我们 T3 出行车联网的架构,可以看到是从底向上,从基础支持到上层不停的赋能,并与车企的信息系统、国家信息平台做交互。作为一家车联网驱动的出行公司,我们收集到了人、车、路等相关的数据,每一种数据都有它自己的应用场景, 数据之间并不孤立,相互赋能,共同支持 T3 智慧出行。
这是我们的存储和计算分离的数据库架构,整个架构分为了两层,一层是计算层,一层是存储层。
在当前存储计算分离的趋势下,我们也是以湖存储为核心,在它周围构建了湖加速湖计算、OLAP 分析、交互式查询、可视化等等一整套的大数据生态体系。
下面是我们 T3 内部对 Hudi 的几个应用场景。
近实时的流式数据处理的 Flink UI 界面上可以看到之前介绍的 DAG 的几个算子都在里面,比如 source、instant_generator 等。
这是我们用 Hudi 构建的增量数据管道。最左侧 CDC 数据捕获之后要更新到后面的一系列的表。有了 Hudi 之后,因为 Hudi 支持索引和增量数据处理,我们只需要去更新需要更新的数据就可以了,不需要再像以前那样去更新整个分区或者更新整个表。
原文链接
本文为阿里云原创内容,未经允许不得转载。