作者:谢云
组织者:李泽驹(Flink中国社区志愿者)
校对:谢云/韩飞(Flink 中国社区志愿者)
本文约6,000 字,建议阅读至少10 分钟。
本文详细介绍了新一代大数据处理引擎Apache Flink的前世今生。
本文来自9 月1 日在成都举办的Apache Flink China Meetup,谢云分享。
本文内容
1.Flink 概述
1.1 Flink 基础知识
1.2 Flink API
1.3 Flink 的用途
1.4 更改链接标题
2. 闪现过去与现在
2.1 Flink 高层API 的历史变化
2.2 Flink API 历史变化
2.3 Flink 检查点恢复过去的变化
2.4 Flink运行时的历史变化
2.5 重建Flink网络栈
2.6 Flink 背压
1.Flink 概述
Flink 是一个分布式计算引擎,也可以用于批处理来处理静态和历史数据集。它还可以处理一些实时数据流。例如,滴滴还可以使用Flink CEP 实时监控用户和驾驶员的行为流,并使用该数据的结果来创建基于事件的应用程序。用户或驾驶员是合法的;
总体来说,Flink 是流上的有状态计算,即数据流上的有状态计算。这里有两个关键词。一种是流。 Flink 认为有界数据集是无界数据流的特例。因此,有界数据集也是一种数据流,事件流也是一种数据流。一切都是流。这意味着您可以使用Flink 处理任何数据,支持批处理、流处理、AI、机器学习等。
另一个关键词是有状态或有状态计算。状态计算是近年来用户要求越来越高的功能。这是一个解释状态含义的示例。例如,如果您的状态是一天内访问您网站的UV 数量,那么您的状态就是一天内访问您网站的UV 数量。 Flink 提供内置的状态一致性处理。这意味着即使任务失败,其状态也不会丢失,也不会过度计算或计算不足,并且提供非常高的性能。
Flink 的流行离不开它的各种标签,包括良好的性能(尤其是在流计算领域)、高可扩展性以及在管理使用内存的纯内存计算引擎方面的诸多优化。还支持条件处理,支持非常大的状态作业(作业状态大小超过TB 在阿里巴巴是很常见的),并且支持一次性处理。
1.1 Flink 基础知识
Flink之所以如此受欢迎,与其最重要的四个基本原理密不可分:检查点、状态、时间和窗口。
首先是Flink最重要的特性:检查点机制。 Flink 基于Chandy-Lamport 算法实现分布式一致性快照,并提供一致性语义。 Chandy-Lamport算法实际上是在1985年提出的,但并未得到广泛应用。然而Flink继承了这个算法。 Spark 最近实现了连续流。连续流的目的也是为了提供这种一致的语义,这就是Chandy-Lamport 算法获得一定流行的原因。行业。
在提供一致的语义之后,为了让用户在编程时更方便地管理状态,Flink 还提供了非常简单清晰的State API,比如BroadcastState 以及最近新增的ValueState、ListState 和MapState。您会自动受益于这种一致的语义。
此外,Flink 还实现了水印机制。它支持基于事件的时间处理或基于系统时间的处理,并且可以容忍数据滞后、数据滞后和乱序数据。
此外,在流计算中,通常会在处理流数据之前根据正在执行计算的窗口类型打开一个窗口。 Flink 提供了多种开箱即用的窗口,包括滑动窗口、滚动窗口、会话窗口以及高度灵活的定制窗口。
1.2 Flink API
Flink Layering API 具有三个主要层,如下所示。
最底层是ProcessFunction,它提供了非常灵活的功能。它可以访问各种状态,并且可以使用计时器回调机制实现一些事件驱动的应用程序。
最顶层是DataStream API,最顶层是SQL/Table API。
1.3 Flink 的用途
Flink 能用来做什么呢?回顾之前的Flink up 站分享,很多嘉宾分享了自己在携程、唯品会、饿了么、滴滴、今日头条等公司基于Flink 的一些实践。应用场景包括实时机器学习、实时统计分析、实时异常监控等。这些实际案例的共同点是都是用来执行实时任务的。
1.4 更改链接标题
Flink 早期的自我介绍是“我是一个开源的流批集成计算引擎”,类似于当时的Spark。 Spark随后被改为一长串带有各种形容词的文本:“我是一个分布式、高性能、高可用、高精度的流计算系统。”最近,Spark已经改为“数据流上的状态计算”。
观察这一变化表明Flink 社区的关注重点发生了转变。这意味着社区目前主要专注于构建流计算引擎。首先在流计算领域扎根,领先其他竞争对手多年,然后利用社区的力量来发展它,然后利用社区的力量来扩大它的生态。
阿里巴巴Flink将自己描述为“Flink是一个处理大量数据的集成引擎”。这个“集成引擎”包括流处理、批处理、AI、机器学习、图计算等。
2. 闪现过去与现在
2.1 Flink 高层API 的历史变化
Flink 1.0.0 期间首先添加了两个框架:Table API 和CEP。与此同时,社区对SQL有着巨大的需求。 SQL和Table API非常相似,都是处理结构化数据的高级语言,并且在实现上可以共享很多内容。因此,在1.1.0中,社区基于Apache Calcite对整个非表模块做了重大重构,让表API和SQL共享很大一部分代码并同时支持。
早在Flink 1.2.0 时代,表API 和SQL 支持翻滚窗口、滑动窗口和会话窗口。
在Flink 1.3.0 时代,首先引入了动态表的概念,可以让你在流和批之间进行转换。流可以是表,表也可以是流。这是流批量集成的基石之一。提现机制是动态表最重要的特性。只有基于回缩,才能正确实现多级应用和多级连接,并保证语义和结果的正确性。同时,该版本还支持CEP操作员可控性。
Flink 1.5.0时代,支持了包括窗口式和非窗口式的Join操作,还增加了SQL CLI支持。 SQL CLI 提供类似shell 命令的对话框,用于交互式运行查询。
2.2 Flink API 历史变化
Flink 1.0.0 期间增加了ValueState、ReducingState、ListState 等State API。 State API 主要允许DataStream 用户更轻松地管理状态。
Flink 1.1.0时代提供了对SessionWindow和后期数据处理的支持。
早在Flink 1.2.0 时代,就提供了一个低级API ProcessFunction。基于ProcessFunction,用户可以更加灵活地实现一些基于事件的应用。
Side Outputs 特性是Flink 1.3.0 时代提供的。一般来说,一个算子的输出只有一种输出类型,但在某些情况下可能有另一种类型,比如以侧流的形式输出异常或延迟的数据,并将其传递到异常节点进行进一步处理。的. 侧面输出。
BroadcastState 在Flink 1.5.0 中重新添加。 BroadcastState用于存储来自上游的广播数据。该节点上许多N 个同时广播状态的数据完全相同,因为它们是从上游广播的。基于这种条件,可以更好的解决不等连接场景。例如,查询“SLECECT * FROM L JOIN R WHERE L.a R.b”表示A 大于B,并且左表和右表中的所有数据都应该相关并输出。
在之前的实现中,没有连接相等条件,因此KeyBy shuffle 只能在一个节点上收集并在单个并发节点上处理。整个工作的瓶颈。
使用BroadcastState 可以进行多项优化。由于左表数据量比较大,右表数据量比较小,所以我们选择广播右表,将左表的key相应均匀分布。对其中之一运行keyby shuffle,并将其洗牌到N 个下游Join 节点。保存两个状态:左状态和右状态。数据流是通过keyby分发的,所以是keyedState。右边的状态是BroadcastState。由于是从上游广播的,所以所有Join节点的BroadcastState中存储的数据是完全一样的。
同时处理所有的keyedState,然后合并keyedState集合以等于左侧数据流的完整处理结果。因此,通过增加Join节点的并发量,可以实现Join节点的可扩展性,提高作业的处理能力。除了不平等加入场景之外,BroadcastState还可以有效解决CAP等动态规则。
Flink 1.6.0时代,提供了State TTL参数和DataStream Interval Join功能。状态TTL 允许您在声明特定状态时指定TTL 参数,以指定该状态在被系统自动清除之前保持多长时间。在此版本之前,如果用户想要实现这种状态清理操作,他们必须使用ProcessFunction 注册一个计时器,并使用计时器的回调手动清除状态。从这个版本开始,Flink 框架现在可以基于TTL 原生解决这个问题。 DataStream Interval Join功能例如是包括左流加入右流之前和之后几分钟的数据的加入。
2.3 Flink 检查点恢复过去的变化
Checkpoint 机制从很早起就得到了Flink 的支持,并且在Checkpoints 和Recallables 被FailOver 取代后,Flink 社区一直在努力提高它们的效率。
早在Flink 1.0.0 时代,就提供了对RocksDB 的支持。该版本之前的所有状态只能存储在进程的内存中。总有一些日子无法保存这段记忆。发生。如果你想存储更多的数据和大量的状态,你应该使用RocksDB。 RocksDB是一个嵌入式、基于文件的数据库,它将数据存储在磁盘上,但同时提供高效的读写能力。因此使用RocksDB时不会出现OOM的情况。 Flink 1.1.0 提供纯异步RocksDB 快照。在以前的版本中,拍摄RocksDB 快照会同步阻止主数据流的处理,从而显着影响吞吐量。这意味着每次检查主数据流时都会卡住。由于纯异步处理后数据流不间断,因此吞吐量也有所增加。
Flink 1.2.0时代引入了可伸缩的key和operator states的概念,支持key状态可扩展性和operator状态可扩展性。
Flink 1.3.0时代引入了一个重要的特性,叫做增量检查点。只有增量检查点才能更好地支持状态非常大的作业。在阿里巴巴内部,这种结核病很常见。如果每次都将每个TB的状态刷新到远程HDFS,效率会很低。通过增量检查点,仅远程发送检查点之间新添加的状态进行存储,通过在每个检查点发送显着更少的数据来提高效率。该版本还引入了粒度恢复。在某些情况下,没有必要恢复整个作业,从而使恢复更加高效。
Flink 1.5.0 期间引入了任务本地状态恢复。基于检查点机制,状态被持久化存储在HDFS等远程存储中,因此如果状态特别大,则必须从远程HDFS重新下载数据。故障转移恢复需要很长时间,因为它需要很长的时间。任务本地状态恢复提供的机制是确保发生作业故障转移时作业状态不会在本地丢失;恢复时只需从远程HDFS 重新创建状态即可,无需下载而直接恢复到本地。提高故障转移恢复效率。
2.4 Flink运行时的历史变化
运行时更改历史非常重要。
早在Flink 1.2.0 时代,就提供了Async I/O 功能。在此版本之前,如果某个任务需要频繁查询访问外部存储(例如查询HBase 表),则每个查询操作都会被I/O 请求阻塞并频繁停止。添加异步I/O 允许同时启动N 个异步查询请求,从而提高总体作业吞吐量。同时,异步I/O保证了作业的异步语义。
Flink 1.3.0时代,引入了HistoryServer模块。 HistoryServer的主要功能是在作业完成后将作业状态和信息归档,以供后续开发人员进一步调查。
在Flink 1.4.0 时代,提供了端到端的一次性语义保证。 Flink所谓的exactly-once一般是指Flink引擎本身的exactly-once。如果您只想执行一次从输入到处理再到输出的整个端到端过程,则需要在输出组件中提供提交函数。旧版本的Kafka 中没有提交功能,但自最近的1.1 版本以来,该功能已经可用,因此Flink 现在一劳永逸地实现了端到端。
在Flink 1.5.0 期间,Flink 首次公开提及新的部署和处理模型。新模型的开发工作在阿里巴巴内部已经进行了两年多,该模型的实现导致Flink内部代码发生了很多变化。自Flink 项目成立以来,运行时变化最大的改进是发生在。所以它的特点之一就是像YARN和Mesos-to-Mesos分离这样的调度系统可以实现动态资源分配、动态释放、更好的资源利用率、更好的作业交付。最后,在这个版本中,Flink对其网络站点进行了基本的重组。
2.5 重建Flink网络栈
有两个指标用于衡量流计算性能:延迟和吞吐量。
一般来说,如果想要更高的吞吐量,就必须牺牲一些延迟,如果想要更低的延迟,就必须牺牲一些吞吐量。然而,网络堆栈的重组同时改善了延迟和吞吐量,这主要是由于该工作的两个方面。一种是基于信用的流量控制,另一种是基于事件的I/O。一种用于增加吞吐量,另一种用于减少延迟。
在实施流量控制之前,现有的网络堆栈必须就位。 Flink的TaskManager用于基于进程来管理各个任务的角色,任务用于基于线程来执行用户代码。当任务之间有数据来回传输时,必须建立网络连接。 Flink 在两个任务管理器之间建立TCP 连接,因为如果在2 秒内建立TCP 连接,就会造成很大的浪费。两个进程之间只有一个连接。 TCP 连接以TCP 通道的形式在任务之间共享,因此整个作业中的TCP 连接永远不会太多。
2.6 Flink 背压
背压意味着如果任务的处理性能跟不上输入速率,则当其输入缓冲区已满时,TCP 读取将被挂起。当TCP读取暂停时,上游出口端的缓冲池会累积,因为此时下游还没有被消耗。
当上游输出端的缓冲池也满时,TCP通道被关闭,并且其内的所有TCP通道也被关闭。因此,上游任务在上游执行增量背压。这就是整个反压过程。因此,Flink 之前的背压机制相对来说比较不原始和粗糙,对整体TCP 性能的控制比较强。如果某个特定任务无法跟上,整个TCP 连接将被关闭。如下所示:
右下方的任务处理跟不上,但上方的任务还能处理。左侧的上游数据仍然可以发送到右上角的任务进行处理。然而,现在整个TCP连接关闭了,右上角的任务也无法接收数据了,整体吞吐量实际上呈下降趋势。优化此功能需要更细粒度的流量控制。目前的优化策略是,如果某个任务无法处理,则只控制该任务对应的TCP通道。其他TCP 通道不受影响。优化实施方法是基于信用的流量控制。
基于信用的流量控制的中心思想是基于信用额度的消费。例如,银行在发放贷款时,会评估每个人的信用额度,并确保贷款金额不超过该人可以支付的金额,以防止不良贷款过多。基于这种方法,可以最大限度地利用银行的资金,同时又不会产生过多的不良贷款。基于信用的流量控制就是基于这个思想。 Flink所谓的信用限额是指下游消费者侧可用的缓冲区数量。如下所示:
MQILBxuPnN3T5f8xNpY%3D" /> 该图左边是指发送端,有四个输出的队列,每个队列里面的方块代表输出Buffer,即准备丢给下游处理的Buffer。右边是消费端,消费端也有四个队列,这四个队列里面也有一些Buffer块,这些Buffer块是空闲的Buffer,准备用来接收上游发给自己的数据。 上面提到基于数据的流控中所谓的信用就是指这个消费端它可用的Buffer数,代表当前还能够消费多少数据,消费端首先会向上游反馈当前的信用是多少, producer端只会向信用额度大于0的下游进行发送,对于信用额度如果为0的就不再发送数据。这样整个网络的利用率便得到了很大的提升,不会发生某些Buffer被长时间的停留在网络的链路上的情况。 基于信用的流控主要有以下两方面的优化提升: 一个是当某一个task发生反压处理跟不上的时候,不会发生所有的task都卡住,这种做法使吞吐量得到了很大的提升,在阿里内部用双11大屏作业进行测试,这种新的流控算法会得到20%的提升; 另一个是基于事件的I/O,Flink在网络端写数据时会先往一个Buffer块里面写数据,这个Buffer块是一个32K的长度的单位,即32K的大小,当这个Buffer块被填满的时候就会输出到网络里面,或者如果数据流比较慢,没办法很快填满的话,那么会等待一个超时,默认一个100毫秒,即如果100毫秒内还没被填满那么这个Buffer也会被输出到网络里面。此时若是在以前版本中Flink延迟可能是在100毫秒以内,最差的情况下是到100毫秒,因为需要到100毫秒等这个Buffer发出去。 如果要得到更低的延时,现在的做法就会将这个Buffer直接加入到输出的队列,但是还是保持继续往这个Buffer块里面写数据,当网络里面有容量时这个Buffer块便会立刻被发出去,如果网络现在也比较繁忙,那就继续填充这个Buffer,这样吞吐也会比较好一点。基于这种算法,Flink的延时几乎是完美的,可以看到它的曲线基本上是低于10毫秒的,这也充分利用了网络的容量,几乎对吞吐没有影响。 作者简介