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

- 最底层抽象简单的提供了状态流处理。该功能主要封装在DataStream API中的Process函数中。用户可以使用这些函数处理多个流中的事件,以及使用一致性容错状态。此外,用户也可以注册事件时间并处理时间回调,允许程序实现复杂的计算。
- 通常,用户不需要直接使用上面的底层抽象,直接使用核心API(例如DataStream API和DataSet API)即可。这些API提供了数据处理的通用模块,比如转换,连接,聚合,开窗,状态等操作。这些API中的数据类型使用对应语言的类表示。DataStream API中的Process函数可以使用底层抽象。DataSet API为绑定数据集提供了更多原语。
- Table API是一个以表为中心的声明式DSL,支持动态修改。Table API遵循关系模型:每个表都有一个schema,API提供诸如select,project,join,group-by,aggregate等操作。Table API很容易扩展,对用户来说也很简洁。此外,Table API代码在执行前还会进行优化。
- 最高层抽象是SQL。这层抽象在语义和表达上与Table API一样,不过是将代码表述成SQL查询。
程序和数据流
Flink程序最基本的构建单元是stream和transformation。从概念上讲,stream是一个数据记录的流,而transformation是一个以一个或多个stream作为输入,产生一个或多个stream的操作。
执行时,Flink程序会映射成流式数据流,它由流和操作组成。每个数据流以一个或多个source开始,以一个或多个sink结束。数据流类似于任意有向非循环图(DAG)。

通常,程序中的transformation操作和数据流中的操作是一对一关系,但有时一个transformation也可能对应多个操作。
并行数据流
Flink程序天生就是并行和分布式的。执行期间,一个流会分成几个流分区,每个操作也分成多个操作子任务。子任务彼此之间是独立的,运行在不同的线程,甚至是不同的机器和容器之中。
子任务的数量就是该操作的并行度。流的并行度等同于操作的并行度。不同操作可能有不同的并行层级。

流可以以一对一(或转发)模式,或者重分发模式在两个操作之间传输数据。
-
一对一流(例如上图中的source与map() 之间)保留元素的分区和顺序。这意味着
map()
操作的subtask[1]
看到的元素与source
操作的subtask[1]
生成的元素一模一样。 - 重分发流(例如上图中的map() 与KeyBy/Window之间)改变了流的分区。根据所选的操作,每个子任务将数据发送到不同的目标子任务。
窗口
聚合事件在流处理和批处理系统上的工作方式是不同的。例如,统计流中的元素个数是不可能的,因为流通常是无限的(未绑定)。一般来说,流上的聚合使用的是开窗函数,诸如“统计最后5分钟”,或者“求最后100个元素的和”。
开窗函数可以是时间驱动的,也可以是数据驱动的。窗口通常分为以下几种:翻滚窗口(没有重叠),滑动窗口(有重叠),会话窗口。

时间
在流处理程序中提到时间,通常说的是以下几种:
- 事件时间,即事件创建的时间。通常使用时间戳表示
- 提取时间指事件进入数据流的时间
-
处理时间指执行操作的时间
状态操作
数据流中绝大多数操作每次只查看一个事件(例如事件解析器),但是某些操作会记录多个事件的信息(例如开窗函数)。这些操作都是有状态的。
状态操作的状态使用一个K-V结构维护。状态会分区,并严格地随着状态操作一起分发。因此,只有使用了keyBy()
函数的stream才能够访问K-V结构的状态,并且仅限于当前操作相关联的值。对齐流和状态的key可以确保所有状态更新都是本地操作,从而确保一致性而不需要额外的事务开销。此外还可以重新分布状态以及流分区。

检查点容错
Flink将stream replay和checkpointing结合起来实现容错。检查点与每个输入流和操作的状态相关。通过回复操作的状态和检查点事件重放,Flink可以从检查点回复数据里,同时保证一致性。
检查点区间是在执行期间用恢复时间(需要重放的事件的数量)来折衷容错开销的手段。
批处理流数据
Flink将批处理程序视为一种特殊的流处理程序。此时流是绑定的(元素数量有限)。这是数据使用DataSet表示,这样处理批数据的方式与流数据的方式相同,除了以下几点:
- 批处理容错不使用检查点。数据恢复会重放整个流。
- 状态操作使用内存/核外数据结构。
- DataSet API使用同步迭代。
网友评论