官方资料: https://ci.apache.org/projects/flink/flink-docs-release-1.7/concepts/programming-model.html
四种不同级别的抽象
Flink提供了4种不同级别的抽象,以供开发流/批处理应用。
-
最底层的抽象简单提供了有状态的流(stateful streaming)。通过Process方法内嵌到DataStream中。
-
DataStream(bounded/unbounded streams)/DataSet(bounded data sets) API等核心api,(实际用的最多)这些api能满足用户指定的transformations, joins, aggregations, windows, state等等的数据处理。
-
Table API,以表为中心的,声明式DSL(Domain Specific Language:A specialized computer language designed for a specific task.),可以动态的改变表(在代表流时)。tables有对应的schema,api提供比较操作,例如:select、project、join、group-by、aggregate 等操作。
可以在表和DataStream/DataSet之间无缝切换,也支持程序混合使用Table API 和DataStream/DataSet APIs。 -
Flink最高级的抽象:SQL,这一层在语法和表达上与Table API类似,但是是以sql查询的形式来represent program。SQL查询可以在Table API定义的表上执行。
程序和数据流(Programs and Dataflows)
构建Flink程序的基本单元是:streams和transformations(注意:DataSet在内部也是一个流)。概念上的流是一组可能永无止境的数据集(a (potentially never-ending) flow of data records),一次转换(transformations)就是用一组或多组流作为输入,然后产生一组或多组流作为输出的操作。
在运行时,flink程序会被映射到streaming dataflows(由streams和transformations operators组成)。每一个dataflow,都是以一个或多个source开始,一个或多个sink结束。dataflow类似与任意的有向无环图(DAGs)。当然特定形式的环可以通过iteration构造。 通常,程序里的转换和dataflow的operators是一一对应的,但有些时候,一个转换,可能对应多个operators。
并行数据流(Parallel Dataflows)
Flink中的程序有并行的、分布式的特点。在执行过程中,一个stream被分成一个多个steam partition,每一个operator都对应一个或多个operator subtasks。operator subtasks之间相互独立,在不同的线程,甚至是不同的机器/容器上执行。
一个特定的operator的并发度就是operator subtasks的数量。一个stream的并发度,总是其producing operator的并发度。同一个程序中不同的operator可能有不同的并发度。
streams可以通过one-to-one的方式或者redistributing(重新分配)的方式在两个operator之间传递数据。
- One-to-One:stream维护着分区以及元素的顺序,map操作的subtask[1]能得到和source operator产生的subtask[1]相同顺序的相同元素。
- Redistributing:stream(map->keyBy/window/apply,或者keyBy/window/apply->sink)的分区发生改变。每一个operator subtask发送数据到不同的目标subtask,取决于所选择的转换。例:keyBy将根据hash重新分区,broadcast(), or rebalance()随机重新分区。
窗口(Windows)
聚合事件(e.g.:sum,count等)的工作方式在流处理和批处理中截然不同。例如,在流处理中想要count所有元素是不可能的,因为流是无界的(unbounded)。在流处理中,聚合操作的作用域(作用范围)称之为窗口(windows),例如:累加最近5分钟的元素,统计最后100个元素之和等。
窗口既可以是事件驱动的(e.g.:每30s),也可以是数据驱动的(e.g.:每100个元素)。窗口一般被分为:tumbling windows(无重叠)、sliding windows(有重叠)、session windows(有间隙的、不连续的)
-
tumbling windows窗口
-
sliding windows窗口
-
session windows窗口
时间
当流编程涉及到了时间(比如为一个窗口定义时间),可能会涉及到不同的时间定义:
- Event Time:一个事件的创建时间,Flink通过timestamp assigners获取时间。
- Ingestion time:事件从source operator进入Flink的dataflow的时间。
- Processing Time:执行操作的时间,由机器的本地时间而定。
有状态的操作(Stateful Operations)
dataflow中的许多操作一次仅关注于一个独立的事件(例如一个事件的解析),而有些操作能记住多个事件的信息(例如窗口操作),这些操作称之为有状态的。
stateful operations的状态维护在可以认为是key-value的键值对中。状态被stream严格的分区和分布( partitioned and distributed)以供stateful operators读。因此,访问state的key/value只能在keyed stream中(通过keyBy函数),只能根据当前事件的key来访问其value。调整对齐stream的key和state可以保证所有state的变更是本地操作,在不需要事务的前提下保证一致性。这种对齐机制,也保证了Flink重新分区state和透明的调整stream分区。
用于容错的Checkpoints(Checkpoints for Fault Tolerance)
Flink通过stream replye和checkpointing的组合方式实现容错。一个checkpoint是一个特定的点–每个输入流的状态和每个operators的状态一致。一个数据流可以从还原点重新开始,只要重新存储状态的operator和从checkpoint回放事件能保持一致性(能保证exactly-once语义)。
checkpoint的间隔是对容错恢复(许多时间需要被回放)的折中。
Streaming上的批处理
Flink将批处理作为流处理的一个特例(有界的流)。DataSet内部被当作流数据处理。因此上面的概念既适用于流处理,也适用于批处理,除了下面几个特例:
- 批处理的容错https://ci.apache.org/projects/flink/flink-docs-release-1.7/dev/batch/fault_tolerance.html:批处理的容错不使用checkpoint,通过重放整个流来恢复。(因为输入的流是有界的)
- DataSet API中有状态的操作(stateful operations)使用内存/外存的数据结构,而不是key-value的键值对索引。
- DataSet API引入了特殊的同步迭代机制(基于superstep-based),只作用域有界的流上。
Flink的分布式运行环境
请参看:这篇文章