简介: 将 Flink 无缝地集成到以 Airflow 和 Hive 为主的批处理系统的技术挑战和应对方案。

本文介绍了 SmartNews 利用 Flink 加速 Hive 日表的生产,将 Flink 无缝地集成到以 Airflow 和 Hive 为主的批处理系统的实践。详细介绍过程中遇到的技术挑战和应对方案,以供社区分享。主要内容为:

  1. 项目背景
  2. 问题的定义
  3. 项目的目标
  4. 技术选型
  5. 技术挑战
  6. 整体方案及挑战应对
  7. 项目成果和展望
  8. 后记

一、项目背景

SmartNews 是一家机器学习驱动的互联网公司。自 2012 年于日本东京成立,并在美国和中国设有办公室。经过 8 年多的发展,SmartNews 已经成长为日本排名第一,美国成长最快的新闻类应用,覆盖全球超过 150 多个国家市场。据 2019 年初统计,SmartNews 的 iOS 和 Android 版本全球累计下载量已经超过 5000 万次。

SmartNews 在过去 9 年的时间,基于 Airflow, Hive, EMR 等技术栈构建了大量的数据集。随着数据量的增长,这些离线表的处理时间在逐渐拉长。另外,随着业务方迭代节奏的加快,对表的实时性也提出了更高的要求。因此,SmartNews 内部发起了 Speedy Batch 的项目,以加快现有离线表生产效率。

本次分享便是 Speedy Batch 项目中的一个例子,加速用户行为 (actions) 表的实践。

APP 端上报的用户行为日志,每日通过 Hive 作业生成日表,这个表是许多其他表的源头,至关重要。这个作业需要运行 3 个小时,进而拉高了许多下游表的延迟 (Latency),明显影响数据科学家、产品经理等用户的使用体验。因此我们需要对这些作业进行提速,让各个表能更早可用。

公司业务基本上都在公有云上,服务器的原始日志以文件形式上传至云存储,按日分区;目前的作业用 Airflow 调度到 EMR 上运行,生成 Hive 日表,数据存储在云存储。

二、问题的定义

1. 输入

新闻服务器每隔 30 秒上传一个原始日志文件,文件上传至相应日期和小时的云存储目录。

2. 输出

原始日志经过 ETL 处理之后,按日 (dt) 和行为 (action) 两级分区输出。action 种类约 300 个,不固定,常有增减。

3. 用户

对这个表的使用是广泛的,多途径的。有从 Hive 里查询,也有从 Presto,Jupyter 和 Spark 里查询,我们甚至不能确定以上就是全部的访问途径。

三、项目的目标

  1. 将 actions 表的时延从 3 小时缩短至 30 分钟;
  2. 对下游用户保持透明。透明又分两个方面:

    • 功能方面:用户无需修改任何代码,做到完全无感
    • 性能方面:新项目产生的表,不应该导致下游读取时的性能下降

四、技术选型

在本项目之前,同事已经对该作业做了多轮次改进,效果不是很显著。

尝试过的方案包括增加资源,投入更多的机器,但遇到了云存储的 IOPS 限制:每个 prefix 最多支持 3000 个并发读写,这个问题在输出阶段尤为明显,即多个 reducer 同时向同一个 action 子目录输出的时候,容易碰到这个限制。另外还尝试了按小时预处理,然后到每日凌晨再合并成日表,但合并过程亦耗时较多,整体时延还是在 2.5 小时左右,效果不够显著。

鉴于服务器端的日志是近实时上传至云存储,团队提出了流式处理的思路,摒弃了批作业等待一天、处理 3 小时的模式,而是把计算分散在一整天,进而降低当天结束后的处理用时。团队对 Flink 有比较好的背景,加上 Flink 近期对 Hive 的改进较多,因此决定采用基于 Flink 的方案。

五、技术挑战

挑战是多方面的。

1. 输出 RC 文件格式

当前 Hive 表的文件格式为 RCFile,为了保证对用户的透明,我们只能在现有的 Hive 表上做 in-place 的 upgrade,也就是我们得重用当前表,那么 Flink 输出的文件格式也得符合 RCFile 格式,因为一张 Hive 表只能有一个格式。

RCFile 属于 bulk format (相对应的是 row format),在每次 checkpoint 时必须一次性输出。如果我们选择 5 分钟一次 checkpoint,那么每个 action 每 5 分钟必须输出一个文件,这会大量增加结果文件数,进而影响下游的读取性能。特别是对于低频 action,文件数会上百倍的增加。我们了解了 Flink 的文件合并功能,但那是在一个 checkpoint 内多个 sink 数据的合并,这并不能解决我们的问题,我们需要的是跨 checkpoint 的文件合并。

团队考虑过以 row format (e.g. CSV) 输出,然后实现自定义的 Hive SerDe,使之兼容 RCFile 和 CSV。但很快我们放弃了这个设想,因为那样的话,需要为每个查询场景实现这个 Hybrid 的 SerDe,例如需要为 Presto 实现,为 Spark 实现,等等。

  • 一方面我们没法投入这么多资源;
  • 另一方面那种方案也是用户有感的,毕竟用户还是需要安装这个自定义的 SerDe。

    我们之前提出了生成一个新格式的表,但也因为对用户不够透明而被否决。

2. Partition 的可感知性和完整性

如何让下游作业能感知到当天这个 partition 已经 ready?actions 表分两级 partition, dt 和 action。action 属于 Hive 的 dynamic partition,数量多且不固定。当前 Airflow 下游作业是等待 insert_actions 这个 Hive 任务完成后,再开始执行的。这个没问题,因为 insert_actions 结束时,所有 action 的 partition 都已经 ready 了。但对于 Flink 作业来说,没有结束的信号,它只能往 Hive 里面提交一个个的 partition,如 dt=2021-05-29/action=refresh。因为 action 数量多,提交 partition 的过程可能持续数分钟,因此我们也不能让 Airflow 作业去感知 dt 级别的 partition,那样很可能在只有部分 action 的情况下触发下游。

3. 流式读取云存储文件

项目的输入是不断上传的云存储文件,并非来自 MQ (message queue)。Flink 支持 FileStreamingSource,可以流式的读入文件,但那是基于定时 list 目录以发现新的文件。但这个方案不适合我们的场景,因为我们的目录太大,云存储 list 操作根本无法完成。

4. Exactly Once 保证

鉴于 actions 表的重要性,用户无法接受任何的数据丢失或者重复,因此整个方案需要保证恰好一次的处理。

六、整体方案及挑战应对

1. 输出 RCFile 并且避免小文件

我们最终选择的方案是分两步走,第一个 Flink 作业以 json (row format) 格式输出,然后用另外一个 Flink 作业去做 Json 到 RC 格式的转化。以此解决 Flink 不能愉快的输出合适大小 RC 文件的问题。

输出 json 的中间结果,这样我们可以通过 Rolling Policy 控制输出文件的大小,可以跨多个 checkpoint 攒成足够大,或者时间足够长,然后再输出到云存储。这里 Flink 其实利用的是云存储的 Multi Part Upload (MPU) 的功能,即每次 checkpoint Flink 也是把当前 checkpoint 攒下来的数据上传至 云存储,但输出的不是文件,而是一个 part。最后当多个 part 达到大小或者时间要求,就可以调用云存储的接口将多个 part 合并成一个文件,这个合并操作在云存储端完成,应用端无需再次读取这个 part 到本地合并然后再上传。而 Bulk format 均需要一次性全局处理,因此无法分段上传然后合并,必须一次性全部上传。

当第二个作业感知到一个新的 json 文件上传后,加载它,转化成 RCFile,然后上传到最终的路径。这个过程带来的延迟较小,一个文件可以控制在 10s 以内,这是可以接受的。

2. 优雅的感知输入文件

输入端,没有采用 Flink 的 FileStreamingSource,而是采用云存储的 event notification 来感知新文件的产生,接受到这个通知后再主动去加载文件。

3. Partition 的可感知性和完整性

输出端,我们输出 dt 级别的 success file,来让下游可靠地感知日表的 ready。我们实现自定义的 StreamingFileWriter,使之输出 partitionCreated 和 partitionInactive 的信号,并且通过实现自定义的 PartitionCommitter,来基于上述信号判断日表的结束。

其机制如下,每个云存储 writer 开始写某个 action,会发出一个 partitionCreated 信号,当它结束时又发出 partitionInactive 信号。PartitionCommitter 判断某一天之内是否所有的 partittion 都 inactive 了,如果是,则一天的数据都处理了,输出 dt 级别的 success file,在 Airflow 通过感知这个文件来判断 Flink 是否完成了日表的处理。

4. Exactly Once

云存储的 event notification 提供 At Least once 保证。Flink 作业内对文件级别进行去重,作业采用 Exactly Once 的 checkpoint 设定,云存储文件输出基于 MPU 机制等价于支持 truncate,因此云存储输出等价于幂等,因此等价于端到端的 Exactly Once。

七、项目成果和展望

项目已经上线,时延维持在 34 分钟上下,其中包括 15 分钟的等待迟到文件。

  • 第一个 Flink 作业需要 8 分钟左右完成 checkpoint 和输出,json 转 rc 作业需要 12 分钟完成全部处理。我们可以把这个时间继续压缩,但是综合时效性和成本,我们选择当前的状态。
  • json 转 rc 作业耗时比当初的预想的要大,因为上游作业最后一个 checkpoint 输出太多的文件,导致整体耗时长,这个可以通过增加作业的并发度线性的下降。
  • 输出的文件数比批作业输出的文件数有所增加,增加 50% 左右。这是流式处理于批处理的劣势,流式处理需要在时间到达时就输出一个文件,而此时文件大小未必达到预期。好在这个程度的文件数增加不明显影响下游的性能。
  • 做到了下游的完全透明,整个上线前后,没有收到任何用户异常反馈。

该项目让我们在生产环境验证了利用流式处理框架 Flink 来无缝介入批处理系统,实现用户无感的局部改进。将来我们将利用同样的技术,去加速更多其他的 Hive 表的生产,并且广泛提供更细粒度 Hive 表示的生产,例如小时级。另一方面,我们将探索利用 data lake 来管理批流一体的数据,实现技术栈的逐步收敛。

八、后记

由于采用完全不同的计算框架,且需要与批处理系统完全保持一致,团队踩过不少的坑,限于篇幅,无法一一列举。因此我们挑选几个有代表的问题留给读者思考:

  • 为了验证新作业产出的结果与原来 Hive 产出一致,我们需要对比两者的输出。那么,如何才能高效的比较两个 Hive 表的一致性呢?特别是每天有百亿级数据,每条有数百个字段,当然也包含复杂类型 (array, map, array等)。
  • 两个 Flink 作业的 checkpoint 模式都必须是 Exactly Once 吗?哪个可以不是,哪个必须是?
  • StreamFileWriter 只有在 checkpoint 时才接受到 partitionCreated 和 partitionInactive 信号,那么我们可以在它的 snapshotState() 函数里面输出给下游 (下游会保存到 state) 吗?
  • 最后一问:你们有更好的方案可供我们参考吗?

原文链接
本文为阿里云原创内容,未经允许不得转载。

SmartNews:基于 Flink 加速 Hive 日表生产的实践的更多相关文章

  1. 基于Bootstrap+jQuery.validate Form表单验证实践

    基于Bootstrap jQuery.validate Form表单验证实践 项目结构 :     github 上源码地址:https://github.com/starzou/front-end- ...

  2. Flink 实战:如何解决生产环境中的技术难题?

    大数据作为未来技术的基石已成为国家基础性战略资源,挖掘数据无穷潜力,将算力推至极致是整个社会面临的挑战与难题. Apache Flink 作为业界公认为最好的流计算引擎,不仅仅局限于做流处理,而是一套 ...

  3. OPPO数据中台之基石:基于Flink SQL构建实数据仓库

    小结: 1. OPPO数据中台之基石:基于Flink SQL构建实数据仓库 https://mp.weixin.qq.com/s/JsoMgIW6bKEFDGvq_KI6hg 作者 | 张俊编辑 | ...

  4. 基于Flink构建全场景实时数仓

    目录: 一. 实时计算初期 二. 实时数仓建设 三. Lambda架构的实时数仓 四. Kappa架构的实时数仓 五. 流批结合的实时数仓 实时计算初期 虽然实时计算在最近几年才火起来,但是在早期也有 ...

  5. Arctic 基于 Hive 的流批一体实践

    背景 随着大数据业务的发展,基于 Hive 的数仓体系逐渐难以满足日益增长的业务需求,一方面已有很大体量的用户,但是在实时性,功能性上严重缺失:另一方面 Hudi,Iceberg 这类系统在事务性,快 ...

  6. Lyft 基于 Flink 的大规模准实时数据分析平台(附FFA大会视频)

    摘要:如何基于 Flink 搭建大规模准实时数据分析平台?在 Flink Forward Asia 2019 上,来自 Lyft 公司实时数据平台的徐赢博士和计算数据平台的高立博士分享了 Lyft 基 ...

  7. 腾讯新闻基于 Flink PipeLine 模式的实践

    摘要  :随着社会消费模式以及经济形态的发展变化,将催生新的商业模式.腾讯新闻作为一款集游戏.教育.电商等一体的新闻资讯平台.服务亿万用户,业务应用多.数据量大.加之业务增长.场景更加复杂,业务对实时 ...

  8. 基于Apache Hudi + Flink的亿级数据入湖实践

    本次分享分为5个部分介绍Apache Hudi的应用与实践 实时数据落地需求演进 基于Spark+Hudi的实时数据落地应用实践 基于Flink自定义实时数据落地实践 基于Flink+Hudi的应用实 ...

  9. 字节跳动流式数据集成基于Flink Checkpoint两阶段提交的实践和优化

    背景 字节跳动开发套件数据集成团队(DTS ,Data Transmission Service)在字节跳动内基于 Flink 实现了流批一体的数据集成服务.其中一个典型场景是 Kafka/ByteM ...

  10. hive内部表、外部表

    hive内部表.外部表区别自不用说,可实际用的时候还是要小心. Hive的数据分为表数据和元数据,表数据是Hive中表格(table)具有的数据:而元数据是用来存储表的名字,表的列和分区及其属性,表的 ...

随机推荐

  1. Linux文件查找、三剑客、正则表达式

    Linux文件查找 1.find查找概述 为什么要有文件查找,因为很多时候我们可能会忘了某个文件所在的位置,此时就需要通过find来查找. find命令可以根据不同的条件来进行查找文件,例如:文件名称 ...

  2. 01.Android崩溃Crash封装库

    目录介绍 01.该库具有的功能 02.该库优势分析 03.该库如何使用 04.降低非必要crash 05.异常恢复原理 06.后续的需求说明 07.异常栈轨迹原理 08.部分问题反馈 09.其他内容说 ...

  3. 如何在Docker容器启动时自动运行脚本

    本文分享自华为云社区<如何在Docker容器启动时自动运行脚本>,作者: 皮牙子抓饭. 如何在Docker容器启动时自动运行脚本 在使用Docker构建应用程序时,有时我们希望在启动Doc ...

  4. Python 汇总列数据到行

    Python汇总Excel列数据到行(方法一) import pandas as pd # 读取Excel文件 df = pd.read_excel('C:\\Users\\liuchunlin2\\ ...

  5. 基于VB6的磁性移动窗体 - 开源研究系列文章

    这次继续整理代码.这个磁性窗体是以前大学的时候开发的,当时模仿的Winamp的效果进行的编程.当时的时候有Windows API函数能够进行处理,但是XP的年代,那个API只是移动的虚框,而不是移动窗 ...

  6. java DES 加密和解密

    代码如下 import javax.crypto.Cipher; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.DESK ...

  7. KingbaseES V8R6 集群运维系列 -- 命令行部署repmgr管理集群+switchover测试

    本次部署未使用securecmd/kbha工具,无需普通用户到root用户的互信. 一.环境准备 1.创建OS用户 建立系统数据库安装用户组及用户,在所有的节点执行. root用户登陆服务器,创建用户 ...

  8. KingabseES执行计划-分区剪枝(partition pruning)

    概述 分区修剪(Partition Pruning)是分区表性能的查询优化技术 .在分区修剪中,优化器分析SQL语句中的FROM和WHERE子句,以在构建分区访问列表时消除不需要的分区.此功能使数据库 ...

  9. 表的唯一约束的作用 KingbaseES VS Oracle

    背景 演示唯一约束怎样创建.删除.禁用和使用唯一性约束,已经多种数据库的差异. 什么是唯一约束 唯一性约束指表中一个字段或者多个字段联合起来可以唯一标识一条记录的约束, 字段中,可以包括空值. 唯一性 ...

  10. 《MySQL技术内幕:InnoDB存储引擎》读书笔记

    SQL语句优化策略 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 WHERE 及 ORDER BY 涉及的列上建立索引. 2.应尽量避免在 WHERE 子句中对字段进行 NULL 值判断,创建 ...