1.5 Apache Flink 基本概念 — 编程模型

Flink 数据流编程模型

1. 抽象级别

Flink 提供了不一样级别的抽象,以开发流或批处理做业。 html

  • 最底层级的抽象仅仅提供了有状态流,它将经过过程函数(Process Function)被嵌入到 DataStream API 中。它容许用户能够自由地处理来自一个或多个数据流的事件,并使用一致的容错的状态,除此以外,用户能够注册事件事件并处理时间回调,从而使程序能够处理复杂的计算。
  • 实际上,大多数应用并不须要上述的底层抽象,而是针对 核心 API(Core APIs)进行编程,好比 DataStream API(有界或无界流数据)以及 DataSet API(有界数据集)。这些API 为数据处理提供了通用的构建模块,好比由用户定义的多种形式的转换(transformations),链接(joins),聚合(aggregations),窗口操做(windows),状态(state)等等。这些API处理的数据类型以类(classes)的形式由各自的编程语言所表示。

底层 过程函数(Process Function)与 DataStream API 相集成,使其能够对某些特定的操做进行底层的抽象。DataSet API 为有界数据集提供了额外的原语,录入循环与迭代。redis

  • Table API 是以 表 为中心的声明式DSL,其中表可能会动态变化(在表达流数据时)。Table API遵循(扩展的)关系模型:表有二维数据结构(schema)(相似于关系数据库中的表),同时API提供可比较的操做,例如select、project、join、group-by、aggregate等。Table API程序声明式地定义了 什么逻辑操做应该执行 而不是准确地肯定 这些操做代码的看上去如何 。 尽管Table API能够经过多种类型的用户自定义函数(UDF)进行扩展,其仍不如 核心API 更具表达能力,可是使用起来却更加简洁(代码量更少)。除此以外,Table API程序在执行以前会通过内置优化器进行优化。

你能够在表与 DataStream/DataSet 之间无缝切换,以容许程序将 Table API 与 DataStream 以及 DataSet 混合使用。数据库

  • Flink提供的最高层级的抽象是 SQL 。这一层抽象在语法与表达能力上与 Table API 相似,可是是以SQL查询表达式的形式表现程序。SQL抽象与Table API交互密切,同时SQL查询能够直接在Table API定义的表上执行。

2. 程序与数据流

Flink程序的基础构建模块是 流(streams) 与 转换(transformations)。(须要注意的是,Flink的DataSet API所使用的DataSets其内部也是流——更多内容将在以后讨论。)概念上来说,流是(可能永无止境的)数据记录流,而 转换 是一种操做,它取一个或多个流做为输入,并生产出一个或多个输出流做为结果。apache

执行时,Flink程序映射到 流数据流(streaming dataflows) ,由 流 以及转换 算符 构成。每个数据流起始于一个或多个 source,并终止于一个或多个 sink。数据流相似于任意的 有向无环图 (DAG) 。虽然经过 迭代 构造容许特定形式的环,可是大多数状况下,简单起见,咱们都不考虑这一点。编程

一般,程序中的转换与数据流中的操做之间是一对一的关系。有时,然而,一个转换可能由多个转换操做构成。windows

3. 并行数据流

Flink程序本质上是并行分布的。在执行过程当中,一个 流 包含一个或多个 流分区 ,而每个 算符 包含一个或多个 算符子任务 。操做子任务间彼此独立,以不一样的线程执行,甚至有可能运行在不一样的机器或容器上。数据结构

算符子任务的数量即这一特定算符的 并行度 。一个流的并行度即其生产算符的并行度。相同程序中的不一样的算符可能有不一样级别的并行度。 编程语言

流在两个算符之间传输数据,能够经过 一对一 (或称 forwarding )模式,或者经过 redistributing 模式:函数

  • 一对一 流(例如上图中 Source 与 map() 算符之间)保持了元素的分区与排序。那意味着 map() 算符的子任务[1]将以与 Source 的子任务[1]生成顺序相同的顺序查看到相同的元素。
  • Redistributing 流(如上图中 map() 与 keyBy/window 之间,以及 keyBy/window 与 Sink 之间)则改变了流的分区。每个 算符子任务根据所选择的转换,向不一样的目标子任务发送数据。好比 keyBy() (根据key的哈希值从新分区), broadcast() ,或者 rebalance()(随机重分区)。在一次 redistributing 交换中,元素间的排序只保留在每对发送与接受子任务中(好比, map() 的子任务[1]与 keyBy/window 的子任务[2])。所以在这个例子中,每一个键的顺序被保留下来,可是并行确实引入了对于不一样键的聚合结果到达sink的顺序的不肯定性。

4. 窗口

聚合事件(好比计数、求和)在流上的工做方式与批处理不一样。好比,对流中的全部元素进行计数是不可能的,由于一般流是无限的(无界的)。相反,流上的聚合须要由 窗口 来划定范围,好比 “计算过去的5分钟” ,或者 “最后100个元素的和” 。优化

窗口能够是 事件驱动的 (好比:每30秒)或者 数据驱动的 (好比:每100个元素)。窗口一般被区分为不一样的类型,好比 滚动窗口 (没有重叠), 滑动窗口 (有重叠),以及 会话窗口 (由不活动的间隙所打断)

更多窗口的案例能够查看这个博客

5. 时间

当提到流程序(例如定义窗口)中的时间时,你能够参考不一样的时间概念:

  • 事件时间 是事件建立的时间。它一般由事件中的时间戳描述,例如附接在生产传感器,或者生产服务。Flink经过时间戳分配器访问事件时间戳。
  • 摄入时间 是事件进入Flink数据流源算符的时间。
  • 处理时间 是每个执行时间操做的算符的本地时间。

更多关于如何处理时间的细节能够查看事件时间文档.

6. 有状态操做

尽管数据流中的不少操做一次只查看一个独立的事件(好比事件解析器),有些操做却会记录多个事件间的信息(好比窗口算符)。 这些操做被称为 有状态的 。

有状态操做的状态保存在一个可被视做嵌入式键/值存储的部分中。状态与由有状态算符读取的流一块儿被严格地分区与分布。所以,只能访问一个 keyBy() 函数以后的 keyed streams 的键/值状态,而且仅限于与当前事件键相关联的值。对齐流和状态的键确保了全部状态更新都是本地操做,以在没有事务开销的状况下确保一致性。这种对齐还使得Flink能够透明地从新分配状态与调整流的分区。

7. 容错检查点

Flink使用 流重放 与 Checkpoint 的结合实现了容错。Checkpoint与每个输入流及其相关的每个算符的状态的特定点相关联。一个流数据流能够能够从一个checkpoint恢复出来,其中经过恢复算符状态并从检查点重放事件以保持一致性(一次处理语义)

检查点间隔是以恢复时间(须要重放的事件数量)来消除执行过程当中容错的开销的一种手段。

更多关于checkpoint与容错的细节能够查看容错文档

8. 流上的批处理

Flink将批处理程序做为流处理程序的特殊状况来执行,只是流是有界的(有限个元素)。 DataSet 内部被视为数据流。上述适用于流处理程序的概念一样适用于批处理程序,除了一些例外:

  • DataSet API中的程序不使用检查点。而经过彻底地重放流来恢复。由于输入是有界的,所以这是可行的。这种方法使得恢复的成本增长,可是因为避免了检查点,于是使得正常处理的开销更小。
  • DataSet API中的有状态操做使用简化的im-memory/out-of-core数据结构,而不是键/值索引。
  • DataSet API引入了特殊的同步(基于superstep的)迭代,而这种迭代仅仅能在有界流上执行。细节能够查看迭代文档
相关文章
相关标签/搜索