目录
1. 抽象层次(levels of abstraction)
2. 程序和数据流(programs and dataflows)
7. 容错的检查点(checkpoints for fault tolerance)
1. 抽象层次(levels of abstraction)
Flink 提供了多种不同层次的抽象来开发流/批程序。
- 最底层的抽象仅仅提供了stateful streaming。它通过过程函数(Process Function)嵌入到DataStream API中。它允许用户自由的处理来自一个或多个流的事件,并且使用一致性容错状态。此外,用户还可以注册事件时间和处理时间回调,使得程序可以识别复杂的计算。
- 在实践中,多数程序不会用到上面提到的低层抽象,而是使用DataStream API(有界/无界流)或者DataSet API(有界数据集)这样的核心API。这些流畅的API提供了数据处理的通用模块,比如用户指定的多种形式的转换操作:join,aggregation,window,state等等。这些API中处理的数据类型在不同编程语言中都以类的形式表达。
底层的过程函数(Process Function)与DataStream API集成,使得可以在底层执行某些特定操作。Dataset API为有界数据集提供了额外的元语,例如 loops/iterations。
- Table API是一种围绕表,且可能会动态改变表的声明式领域专用语言(DSL)。Table API遵循下述关系模型:表有一个附加的schema(类似于关系型数据库),并且提供了相应的操作,例如,select,project,join,group-by,aggregate等。Table API程序声明式的定义了哪些逻辑操作需要做,而不是指定操作代码应该是什么样。尽管Table API可以通过多种类型的用户自定义函数扩展,但与Core API相比还是缺乏表现力,不过用起来更简洁。此外,Table API在执行前会通过优化器(该优化器应用了一些优化准则)做一些优化。
table 和 DataStream/Dataset之间可以无缝转换,使得程序可以混合使用Table API和DataStream API,DataSet API。
- Flink提供的最高层次的抽象是SQL.这个抽象在语法和表达形式上都与Table API相似,但作用是将程序表示为SQL查询语句。SQL抽象与Table API交互紧密,SQL查询可以在Table API定义的表上执行。
2. 程序和数据流(programs and dataflows)
Flink程序最基本的编程模块是stream和transformation.(Flink DataSet API中使用的DataSet在内部也是stream - 稍后再详细讨论)。从概念上讲, stream是数据记录流(可能永远不会结束),transformation是一种操作,它将一个或多个stream作为输入,产生一个或多个stream作为输出。
执行时,Flink程序会被映射成streaming dataflow,由streams和transformation operator(转换操作)构成。每一个dataflow都开始于一个或多个source,结束于一个或多个sink。类似于有向无环图(DAG)。尽管通过迭代结构特殊形式的循环是可以实现的,但是多数情况下我们都隐士的隐藏了这些操作。
通常情况下,程序中的转换操作和dataflow中的算子是一一对应的。不过,有时候,一个转换操作可能由多个转换操作算子构成。
Sources和Sinks的相关文档请参阅streaming connectors和batch connectors。转换操作参阅 DataStream operators和DataSet transformations.
3. 并行数据流(parallel Dataflows)
Flink 本质上就是并行和分布式的。在执行期间,每一个stream都有一个或多个 stream分区,每一个operator都有一个或多个operator subtasks。这些operator subtasks之间是相互独立的,在不同的线程,甚至不同的机器或容器中执行。
operator subtasks的数量就是对应operator的并行度(parallelism)。一个stream的并行度是它产生的operator的数量。同一程序的不同operator可能会有不同水平的并行度。
Stream可以在两个operator之间以一对一或重分配的形式传输数据:
- 一对一stream保留了原有的分区和元素顺序(例如Source和map()操作)。这意味着map() operator的subtask[1]接收到的数据以及数据的顺序与Source operator的subtask[1]产生的一致。
- 重分配stream会改变stream的分区(上述map()和keyBy/window之间,keyBy/window和sink之间)。根据不同的transformation,每个operator subtask向不同目标subtask发送数据。例如keyBy()(通过对key 哈希进行充分区),rebalanse()(随机重分区)。在重分配交换中,元素的顺序只在对应发送和接收的suntask中有保留(例如map()的subtask[1]和keyBy/window的subtask[2])。所以,在这个例子中,每个key中数据保留了顺序,但是到达sink的不同key的聚合结果的顺序是没有保障的。
配置和控制并行度的更多信息请参阅并行执行文档。
4. 窗口(Window)
聚合事件(例如count,sum)在stream和batch处理上有所不同。例如,统计stream中元素的个数是不可能的,因为流通常是没有边界的。所以流聚合(count,sum)使用窗口(window)划定范围,例如,统计过去5分钟内元素的个数,或者最近100个元素的和。
Windows可以是时间驱动(示例:每30秒)或数据驱动(示例:每100个元素)。通常可以区分不同类型的窗口,例如滚动窗口(没有重叠)、滑动窗口(有重叠)和会话窗口(由不活动的间隙隔开)。
更多window示例可以查看blog post.更多细节请参阅window文档。
5. 时间(Time)
当提到streaming程序中的时间时,可以想到不同的时间:
- 事件时间:事件创建的时间。通常描述为事件中的时间戳。例如,产生数据的传感器附加的时间,或者生产者服务附加的时间。Flink通过 timestamp assigner获取时间戳。
- 摄入时间: 事件在source operator进入flink dataflow的时间。
- 处理时间:operator执行基于时间操作时的本地时间。
关于如何处理时间的更多细节请参阅event time文档。
6. 有状态操作(stateful operations)
数据流中的一些操作只关注某一时刻的一个单独事件,而另一些操作则记录了多个事件的信息,这些操作称作有状态操作。
有状态操作的状态维护在一个嵌入式key/value存储中。这个状态是分区的,并且和有状态操作符读取的数据一起分区。因此,只能在应用了keyBy()函数之后的有键流上访问key/value状态,并且只能访问与当前时间的key关联的值。给流和状态取别名,可以保证所有的状态更新是本地操作,在没有事务负担的情况下保证一致性。这种对其操作使得Flink可以显示的分配state,调整分区。
更多信息请参考state。
7. 容错的检查点(checkpoints for fault tolerance)
Flink通过stream replay和checkpointing组合实现容错。检查点与每个input stream中一个特殊点以及每个operator的对应状态有关。streaming dataflow可以从检查点恢复,并且,通过存储operator的状态,回放检查点中的事件可以保证数据一直性(exactly-once processing 语义)。
检查点间隔是一种将执行期间的容错开销与恢复时间(需要重放的事件数量)进行权衡的方法。
容错内部构件的描述提供了关于Flink如何管理检查点和相关主题的更多信息。启用和配置检查点的详细信息见检查点API文档。
8. 流的批处理(Batch on Streaming)
Flink将批处理程序作为流程序的一种特殊情况执行,其中流是有界的(元素数量有限)。数据集在内部被视为数据流。因此,上述概念同样适用于批处理程序,但有少数例外:
- 批处理程序的容错不使用检查点。恢复通过完全重播流来实现。这是可能的,因为输入是有界的。这将使成本更接近于恢复,但会降低常规处理的成本,因为它可以避免检查点。
- 数据集API中的有状态操作使用简化的内存/内核外数据结构,而不是键/值索引。
- 数据集API引入了特殊的同步(基于前一步的)迭代,这只在有界流上是可能的。详细信息,请查看迭代文档。
注:转载请注明出处。
参考:https://ci.apache.org/projects/flink/flink-docs-release-1.6/concepts/programming-model.html