理解Flink(一):数据流编程模型

来源:互联网 发布:程序员鄙视链图 编辑:程序博客网 时间:2024/06/07 09:41

http://blog.csdn.net/u011239443/article/details/71027507

抽象级别

Flink提供了不同的抽象级别来开发流/批处理应用程序。

这里写图片描述

  • 最低级抽象只提供有状态的流。它通过流程函数(Process Function)嵌入到DataStream API中。它允许用户从一个或多个流中自由地处理事件,并使用一致的容错状态。另外,用户可以注册事件时间和处理时间回调,允许程序实现复杂的计算。
  • 在实践中,大多数应用程序不需要上面的低层次抽象描述,而是将项目对Core APIDataStream API(有限/无限流)和 DataSet API(有限的数据集)。这些常用的api提供了数据处理的通用构件,如各种形式的用户指定的转换、连接、聚合、窗口、状态等。在这些api处理的数据类型,表示为在相应的编程语言的类。低层次的处理功能(Process Function)与DataStream API集成在一起,这使得仅针对某些操作进行低级抽象就成为可能。DataSet API为有界的数据集提供了额外的原语,比如循环/迭代。
  • Table API是一种以表为中心的声明性领域专用语言(DSL),它可以动态地更改表(在表示流时)。Table API遵循(扩展的)关系模型:表有一个附加模式(类似于关系数据库表)和API提供了类似的操作,如select, project, join, group-by, aggregate等。Table API程序以声明的方式定义逻辑操作应该做什么,而不是指定操作的代码看起来如何。虽然Table API可以由各种类型的用户定义的函数来可扩展的,它的表达能力低于Core API,但使用更简洁(写更少的代码)。此外,Table API程序还在执行前,通过优化器使用优化规则。表和 DataStream/DataSet之间可以无缝地转换,允许Table APIDataStream、DataSet API混合编程。
  • Flink提出的最高层次的抽象是SQL。这个抽象在语义和表达中,类似于的Table API,但是表示程序的是SQL查询表达式。SQL抽象与Table API紧密地交互,SQL查询可以在Table API定义的表上执行。

程序与数据流

Flink程序的基本构建块是流和转换(streams and transformations)。(注意,Flink的DataSet API中使用的DataSet的内部也是流-稍后详细说明。)概念上,一个流是一个(可能无止境的)数据记录流。转换是一个操作,一个或多个流作为输入,并生成一个或多个输出流作为结果。

执行时,Flink程序映射到流动的数据流(streaming dataflows),它由流(streams)和转换操作(transformation operators)符组成。每个数据流从一个或多个源(sources)开始,结束在一个或多个水槽(sinks)。数据流像任意的有向无环图(DAG)。尽管通过迭代构造可以允许循环,但在大多数情况下,我们会为了简单而忽略它。

这里写图片描述

程序中的转换和数据流中的操作之间,经常存在一一对应的。然而,有时,一个转换可能由多个转换操作组成。

并行数据流

Flink的程序本质上是平行的和分布式的。在执行期间,流有一个或多个流分区(stream partitions),每个操作符都有一个或操作子任务(operator subtasks)。操作符子任务彼此独立,并在不同的线程中执行,可能在不同的机器或容器中执行。

操作子任务的数量是那个特定操作的并行度(parallelism)。流的并行度通常是它的生产操作。同一程序的不同操作可能有不同级别的并行度。

这里写图片描述

流可以传输在两个操作之间传输数据通过一对一(one-to-one)(或者传递)的模式和重新分配(redistributing)模式:

  • one-to-one的流(例如上图中,Sourcemap()操作之间)保留分区和排序的元素。这意味着子任务的map()操作,以Source操作的子任务所产生的同一顺序,将看到相同的元素。

  • redistributing的流(如上map()keyBy/window之间,以及keyBy/windowSink之间)改变了流的分区。每个子任务操作将数据发送到不同的目标子任务,这取决于所选的转换。如keyBy()(通过hashing key重新分区)、broadcast(), 或rebalance()(随机重分区)。重新分配交换元素之间的顺序,只是保存在每一对发送和接收子任务(例如,map()的子任务的keyBy/window的子任务)。因此,在这个例子中,每个键的顺序都被保留了,但是并行性确实也使得不同键的聚集结果到达汇聚点的顺序的变得不确定。

窗口

聚合事件(如count, sum),在流处理和批处理上有不同的工作方式。例如,不可能计算流中的所有元素,因为流通常是无限的(无界的)。相反,聚合流(count, sum等),由窗口(windows)来划分范围,如“计算过去5分钟”,或“过去100元素的总和”。

Window可以是时间驱动(如:每30秒)或数据驱动(如:每100个元素)。通常区分不同类型的窗口有:翻滚窗口(tumbling windows)(没有重叠),滑动窗口(sliding window)(重叠),和会话窗口(session window)(被一个不变间隔分隔)。

这里写图片描述

Time

流程序中的时间(例如定义window),有不同的时间概念:

  • 事件时间(Event Time)是创建事件的时间。它通常由事件中的时间戳来描述,例如由生产传感器或生产服务所连接的时间戳。通过时间戳分配器,Flink访问事件的时间戳。
  • 摄入时间(Ingestion time)是source操作,一个事件的时候进入Flink 数据流的时间。
  • 处理时间(Processing Time)是执行基于时间的操作的每个操作的本地时间。

这里写图片描述

状态操作

虽然许多数据流中的操作一次只是关注一个事件(例如事件解析器),一些操作记录了跨多个事件的信息(例如窗口操作)。这些操作称为有状态的(stateful)。

状态操作的状态,可以被认为被保存在嵌入的key/value存储中。状态被分区,并与状态操作读取的流严格地分布在一起。因此,访问key/value状态,只有可能是有key流,在keyBy()函数之后,并被限制于当前事件key所对于的value。对齐流和状态的键,确保所有状态更新都是本地操作,确保一致性,却没有事务开销。这一对齐还允许Flink重新分配状态,并透明地调整流分区。

这里写图片描述

容错检查点

Flink使用了流重演和检查点的组合来实现容错。检查点与每个输入流中的特定点以及每个操作对应的状态相关。通过恢复操作状态和重演检查站的事件,流动的数据流可以从一个检查点恢复,同时保持一致性(仅一次处理语义)。

检查点间隔的设置需要权衡执行期间容错的开销与恢复时间(需要被重演事件的数量)。

流上的批处理

Flink将执行批处理程序作为一种特殊的流程序,这种情况下流是有界(有限数目的元素)。DataSet被内部视为数据流。上面的概念同样适用于批处理程序和流程序,但也有例外:

  • DataSet API中的程序不使用检查点。恢复是通过完全重演流来实现的。这是可能的,因为输入是有界的。这使得恢复的成本更贵,但是使
    常规的处理更廉价,因为它避免了检查点。
  • DataSet API中的状态操作,使用简化的in-memory/out-of-core的数据结构,而不是key/value索引。
  • DataSet API引入了特殊的只可能基于有界流的同步(superstep-based)迭代,。

这里写图片描述

0 0