官方服务微信:dat818 购买与出租对接

深入剖析流批一体中Lambda架构的优缺点及面临的问题

3万

主题

2

回帖

10万

积分

管理员

积分
104110
发表于 20 小时前 | 显示全部楼层 |阅读模式
    谈及流批结合模式,便不可避免地要讨论架构问题。各家公司均拥有各自的离线计算策略,这一领域经过多年发展,已趋于成熟且值得信赖。然而,其不足之处在于难以满足日益增长的实时性需求。因此,人们又引入了另一条实时数据处理路径。在实时计算探索的早期阶段,结果的精确性难以得到保障,需借助离线计算结果进行校准和调整。最终,由业务部门负责将这两个结果进行整合,这正是我们常说的架构。

    其不足之处显而易见,首先必须配备两组计算工具,一组负责提供计算能力,另一组则负责Batch处理能力。由于不同工具所提供的API存在差异,因此业务部门必须开发两套不同的业务处理逻辑。此外,由于不同工具在处理过程中可能存在行为上的差异,特别是在标准尚未明确规定的情形下,两条计算路径所得到的结果一致性也难以得到确保。

    在过去的数年间,我们持续努力应对由架构设计引发的种种难题,图中详细展示了其中三个颇具代表性的项目案例。

    起初,我们内部接到的那些提出流批一体化需求的业务方,他们对业务处理的实时性有着较高的期待。他们期望在保持实时性的基础上,通过采用 Flink Batch 技术,来提升开发工作的效率。

    此外,我们还注意到社区在流批结合领域给予了高度关注。Flink 在其近期的各个版本中,几乎每个版本都加强了流批结合的功能。如上图所示,这是去年从阿里云分享的一篇文章中摘录的内容。而今年,社区在批处理方面也开展了众多工作,例如预测执行以及更多类似的技术。这幅图未能展现部分最新成果,但从中仍可明显看出,Flink 在其API层、中间层、调度层以及底层等方面,已进行了大量关于流批一体化的工作。因此,我们决定采用Flink进行流批一体化处理,并确立了与社区共同开发、共建的模式。

    在项目内部实施阶段,我们得到了社区的大力协助和诸多灵感。我所分享的成果,正是与社区紧密协作的结晶。此外,经过我们内部对众多功能的验证,这些功能已升级为社区版,旨在让更多用户受益。

    02

    流批一体在快手的规划

    我们最初接到的关于流批结合的请求,源自于两个核心业务领域:机器学习与数据集成。在机器学习领域,我们内部Flink平台一直是业务的重中之重,用户借助Flink进行实时特征的计算和样本的实时拼接。他们期望通过Flink的批处理功能,能够复用一套业务逻辑,以满足数据回溯的需求,进行数据修正,并生成历史数据。而且用一个引擎也可以避免结果不一致的问题。

    数据同步团队提出了另一项需求,他们负责的数据同步产品需在异构数据源间进行数据同步操作,该操作分为离线同步与实时同步两种。在之前的老架构中,离线同步主要依赖MR和DataX技术,而实时同步则基于MR与自研框架。然而,这种架构存在计算能力不足、扩展性不强等缺陷。因此,数据同步团队正致力于利用 Flink 的计算效能与扩展性优势,开发新一代的数据同步产品。

    我们认为流批一体的终极目标有以下几点。

    为了达成这些目标,我们必须对框架进行大幅度的优化升级,并采纳分步骤构建的策略,逐步推进这些目标的实现。

    首个阶段旨在为当前对流批结合有需求的两大业务部门提供支持,确保他们能够享受到一致的用户体验和计算引擎,以便用户能够率先投入使用。我们的规划重心在于提升 Flink 的批量处理能力,确保产品入口的顺畅运作,并对业务需求提供紧密的跟进与支持。

    第二阶段的目标在于提升产品的易用性,这涉及智能化的驱动引擎、卓越的批量处理能力,以及统一的存储系统。同时,产品还需具备满足更广泛、更复杂业务需求的强大支持。

    03

    第一阶段(加强批能力)的进展

    我们的第一期用户主要来自机器学习领域和数据同步领域。对于批处理能力,用户强调稳定性是确保生产可用性的关键,同时他们也期待在易用性方面有所改进。在这个阶段,他们对性能和功能的要求相对较低,只要大部分情况下没有明显的性能下降即可。在第一阶段,我们着重于提升内部能力建设,将焦点放在了系统的稳定性和用户友好性上,这正是我今天想要重点阐述的部分。此外,值得一提的是,在性能方面,我们同样对 Hive 的 /Sink 部分进行了优化处理。对此感兴趣的听众,不妨关注生产实践专场中的《Hive SQL 迁移 Flink SQL 在快手的实践》这一分享内容。

    以下是 Flink Batch 稳定性的核心问题:

    首先,分布式环境中可能会遇到慢节点问题。这种情况通常有两方面的原因。一方面,某些节点承担的并发任务量超过了其他节点;另一方面,在任务量相近的情况下,部分节点的并发执行速度较慢。

    这两种策略存在显著差异,在本次交流中,我们将重点探讨第二种问题解决方法。这涉及到在批处理模式下,如何应对由机器故障或网络问题导致的处理缓慢的节点。如图所示,系统中有三个并行处理单元,它们各自处理的数据量大致相同。然而,由于第三个处理单元所在的机器负载过重,其聚合操作的数据处理速度明显低于前两个并行单元。

   


    因此,Flink采纳了推测执行机制以应对处理速度较慢的节点问题,这一方法与Spark等传统框架的解决思路相仿。一旦识别出尾部耗时任务,便会在非繁忙服务器上启动相应任务的镜像副本。如图所示,Agg3在另一台服务器上启动了一个镜像副本,记作Agg3'。执行顺序取决于哪个副本先完成,随后将其他副本的结果取消。

    此处我们假设后续启动的实例率先完成运行,这意味着仅其生成的数据对后续环节可见,同时还将取消原有的Agg3。

    上图揭示了推测执行在架构层面上的具体实现过程。其中,整个架构中必须包含以下几个关键组件:

    在框架结构方面,我们还需确保推测执行得以实现。实现推测执行的目标在于,通过调整框架层,无需对每个模块进行额外的开发工作。在Flink中,任务的执行与数据分片的分配并非在编译阶段就确定,而是在运行过程中,每个任务在处理完一个数据分片后,再申请下一个分片。

    在实施推测执行机制之后,为确保数据准确性,新启动的镜像实例需与原有实例处理相同的分片集合,因此我们在缓存中记录了子任务与已分配集合之间的对应关系。

    如图所示,sub-task1 已完成了对某些任务的处理,并正在对其他任务进行操作。然而,由于处理速度较慢,该任务被认定为慢速节点。因此,在另一台机器上启动了一个镜像实例,具体为‘’。在申请分割操作时,会先从缓存记录中移除已处理过的sub-task信息,并且只有当sub-task1完成了既定的路径后,才会为其分配新的split,同时会将这一新的split信息添加至缓存记录中。

    与某些不同,众多Sink类型在写入或提交时需要额外开发以规避冲突。目前,在我们的内部系统中,对File Sink和Hive Sink这两种Sink类型已实现了推测执行功能。

    此外,我们对临时文件的组织策略进行了调整,在文件组织目录中增加了实例的示例信息。待所有并发任务结束后,仅将执行速度最快的实例所生成的临时文件转移至正式目录,而其他执行较慢实例所生成的文件将被删除。由于时间限制,Sink 对推测执行的支持尚未纳入1.16版本,预计将在1.17版本中推出。届时不仅会对Sink提供支持,而且还将对FLIP-143引入的New Sink给予相应的支持。

    第二个方面,存在波动。Flink批处理作业涉及两种数据传输途径,其一为无需存储于磁盘,其二则为需存储于磁盘。在第一种方式中,上游任务需将数据写入离线文件,待下游任务启动后,再对已写入文件的数据进行读取。这些数据既可存储在本地磁盘,亦能存放在远程服务器上。

    Flink 社区版提供了两种实现方式。首先,一种方式是将上游计算节点的数据写入本地磁盘,而下游节点则连接到上游节点上以读取这些文件。因此,在计算任务完成后,无法立即终止,必须等待下游节点完成对文件的消费,才能释放资源。这种方式不仅导致资源浪费,而且容错成本较高。

    第二种方案是独立集群模式,该模式通过独立集群为数据提供专门的服务,有效解决了资源利用率不高和容错成本过大的问题,同时,它还采纳了云原生理念,实现了存储与计算的分离。

    公司内部已实现类似功能,鉴于此,我们决定利用现有技术以减少开支,最终采纳了内部解决方案,该架构主要由五个不同角色构成。

    观察内部实现与社区版本,二者大体相同,但存在一项关键差异。内部所采用的技术,与社区所发布的版本有所不同,社区当前版本采用的是Map技术。

    Map中的数据源自一个上游的处理任务,它有可能被多个后续的处理任务所使用。相比之下,另一部分数据则是由多个上游的计算任务共同生成,并且仅被一个下游任务进行并发处理。

    其优势在于下游的读取是按照顺序进行,这避免了随机的小型输入输出操作,并且减轻了磁盘的负担。然而,为了防止数据不可用的情况发生而不得不重新启动所有的上游映射任务,通常会选择创建多个副本,这样做虽然会增加存储成本。不过,由于临时数据存储的周期相对较短,因此这种多副本带来的额外开销是可以接受的。

    第三个,离线任务稳定性差。主要有两个原因。

    针对首个问题,引擎需辨别因资源抢占引发的问题与其它业务异常引起的失败。资源抢占引起的异常可由框架自动进行重试,此类重试不计入失败重启的次数。图中展示了内部实现细节需进行的两项调整:首先,需将退出码传递给AM;其次,ler需识别每次失败的具体原因,并主动忽略某些特定类型的异常,同样这些异常不计入失败重启的次数。

    针对第二个关于恢复成本高昂的问题,图示的左侧部分展示了当前的处理手段。若某项作业需分四个子任务执行,然而由于资源匮乏,仅能获得两个资源支持,故初期仅能启动前两个子任务进行操作。待这两个子任务完成所有工作后,将资源转交予第三个和第四个子任务。然而,这两个子任务发现所剩工作量不多,随即退出。此类资源分配不均的问题,往往会导致高昂的成本。

    为避免付出过高的成本,我们对每个task所能处理的数量设定了上限,当某个task处理的数据量触及既定限制,该task便会自行停止工作,并将剩余的数据量转交给后续的task继续处理。

    在实施过程中,需留意若干细节。例如,若任务执行不力,需将之前处理过的内容退回,同时,还需相应地扣除记录中的数量,以防止信息泄露问题的发生。

    我们在易用性上的工作可以提炼成以下三点。

   


    在推进流批一体化业务的同时,我们也在探索如何大规模推广 Flink 批处理技术。众多业务领域实际上都存在对流批一体的需求,然而它们仍在持观望态度,担忧 Flink Batch 的通用性是否能够适应其他领域,是否适用于不同的业务场景。此外,我们自身也需要找到一种方法来验证并预先评估其适用性。因此,我们决定将 Flink Batch 集成到我们的离线生产系统中,使得 Flink Batch 能够承担线上离线任务的处理。

    图中呈现的是快手平台的离线生产引擎结构,我们进行的所有离线SQL生产任务,均选用Hive作为主要工具,并且将其作为统一的接入点。观察图示,可以发现一个专门负责智能路由的组件,该组件负责依据既定规则,将SQL作业精确地导向底层引擎。目前,底层引擎已成功对接了spark、mr以及其他相关引擎。

    我们将Flink系统也纳入了该架构之中。该系统具有极高的灵活性,因此我们能够轻松地定制多样化的路由策略。例如,白名单机制能够确保仅将特定的SQL任务定向至Flink平台执行;而黑名单机制则用于屏蔽部分任务,如当前Flink尚不兼容的SQL语句;在初始阶段,优先级机制仅将优先级较低的作业分配给Flink;此外,灰度机制能够控制将一定比例的少量任务导向Flink引擎。这些策略使得我们能够逐步提升 Flink 在新增及现有离线作业中的应用比例。

    在 Flink 接入离线生产引擎里,我们有三项关键工作。

    经过不懈努力,我们已成功完成首个阶段的任务,累计完成了3000项离线作业,这些作业涵盖了机器学习、数据集成以及离线生产等多个业务领域。

    04

    第二阶段(业务视角的流批一体)的挑战

    第一个挑战在于需手动设定操作模式,这包括确定作业执行模式、交互模式以及是否启用预测执行等。用户需掌握众多底层知识,以便识别所需参数,并合理地调整这些参数设置。

    我们认为,为了提升用户体验,用户无需深入了解或感受到过多的底层细节,因此,我们的方案分为两个主要方向。首先,我们致力于简化开箱时用户需要调整的设置;其次,我们计划引入智能化的交互手段。用户只需明确业务逻辑及结果的处理方式,其余的将由引擎自动推导完成。

    第二个挑战关乎批处理效能的提升。为了使 Flink 批处理具备更广泛的应用领域,其性能必须实现显著飞跃。在最新发布的 1.16 版本中,Flink 社区已经启动了多项 FLIP 项目,旨在推动 Flink Batch 性能的进一步提升。

    在调度层优化方面,我们首先关注的是动态自适应执行。目前,社区已经展现出一定的自适应能力。接下来,社区计划进一步拓展其自适应能力,使其更加灵活。例如,在执行过程中,社区将能够根据join操作两端的实际数据量,灵活地调整join的策略。回顾先前的 Batch,该工具仅对拓扑结构中的边进行动态调整,然而在调整 join 策略时,则需要动态改变拓扑的整体布局。这一变革甚至可能跨越优化器与调度器之间的界限。

    第二个方面涉及到的调整,这里以一个具体的案例来说明,即通过运行时获取的信息进行进一步的筛选,在1.16版本中,社区对此进行了探索,并且,在TPC-DS测试中,这一优化措施已经显现出显著的成效。然而,目前的方法仅限于在特定场景下进行运行时的分区缩减。然而实际上,其他类似的操作同样适用于运行时的筛选,并且在整个连接过程中,每一级连接都可以进行此类优化,以实现性能的增强。

    第三个改进之处在于对Flink批处理作业的优化,此前Flink的批处理作业要么采用方式一,要么采用方式二,方式一的优势在于上下游间的数据无需落盘,从而提升了性能。然而,它的不足之处在于资源消耗较大、容错成本较高。而方式二的优缺点则与方式一恰好相反。为了整合这两种方式的优点,1.16版本推出了新的解决方案。

    第四个改进之处涉及到了技术层面的优化,众多新兴的查询引擎普遍采用了向量化的执行方式和以数据为核心进行代码生成的方法。然而,在 Flink 中引入这些技术,却需要对整个框架进行大幅度的调整。因此,在正式应用之前,必须进行详尽的POC(Proof of )测试,以评估方案实施的复杂性,并确定其带来的可量化性能提升。

    下面分享流批混跑的业务场景下遇到的两个挑战。

    首个情况涉及带有状态的任务在流批混合运行中的实现。举例来说,在批处理模式下完成历史数据的处理之后,流处理模式将紧接着进行增量数据的消费。目前,这种流批混合运行仅限于部分无状态SQL作业。而对于有状态的SQL作业,这种混合运行方式尚不支持。原因在于,批处理模式下的算子并不与状态进行交互,且在批处理作业执行完毕后,也不会产生状态数据。因此,在批处理作业完成后,流处理作业启动时,将无法确定如何初始化各个算子的状态。

    为了达成这一目标,无论是流处理模式还是批量处理模式,算子均需与状态进行交互。鉴于性能方面的考量,在批处理模式下,算子于执行完毕后可将数据导出到状态中,以此减少与状态之间的频繁交互。

    在流批混跑的环境中,还有一个难题,那就是当批处理作业产生的状态数据量巨大时,流式作业启动时如何迅速从快照中恢复。这一问题在纯粹的流式业务场景中同样存在,例如,当状态数据量庞大时,作业一旦发生重启,恢复状态所需的时间便会相当长。由于 Flink 采用本地状态,在恢复过程中必须将所有状态预先迁移至本地,再将其导入计算模块中,这无疑是一个颇具挑战性的问题。

    流批混跑的第二重考验在于存储的分裂现象。尽管业务逻辑能够通过统一的计算工具进行表述,但存储方式却存在差异。流处理通常依赖于 Kafka 消息队列进行输入和输出操作,而批处理则通常采用 Hive 或 DFS 文件系统进行输入和输出。这种存储的分裂导致了复杂性,使得用户和平台需要承担更多的责任。用户需提炼出逻辑映射表,并明确逻辑表与基础物理表的对应关系,随后,平台层依据运行模式将逻辑表导向相应的物理表,而引入一套集流式和批处理于一体的统一存储系统,已成为不可或缺的关键步骤。Flink 社区去年推出了 Flink Table Store,这是一种集流处理与批量处理于一体的存储解决方案,它不仅能够实现流的写入和读取,还能支持批量的写入和读取,从而在存储层面消除了流与批处理之间的区别。

    关于Flink批处理技术的诸多疑问,欢迎扫描二维码,加入我们的社区钉钉交流群进行探讨。

    往期精选
您需要登录后才可以回帖 登录 | 立即注册

Archiver|手机版|小黑屋|关于我们

Copyright © 2001-2025, Tencent Cloud.    Powered by Discuz! X3.5    京ICP备20013102号-30

违法和不良信息举报电话:86-13718795856 举报邮箱:hwtx2020@163.com

GMT+8, 2025-5-12 21:02 , Processed in 0.100117 second(s), 17 queries .