flink学习笔记(一)——数据流编程模型

flink官方文档学习笔记,本文主要是flink一些基础概念

数据流编程模型(Dataflow Programming Model)

抽象等级(Levels of Abstraction)

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

  • Statefule Stream Processing:是最低级别(底层)的抽象,只提供有状态的流。它通过ProcessFunction嵌入到DataStream API之中。它使得用户可以自由处理来源于一个或者多个流的事件
  • DataStream/DataSet API:在我们的实际工作中,大多数的应用程序是不需要上文所描述的低级别(底层)抽象,而是相对于诸如DataStream API(有界/无界流)和DataSet API(有界数据集)的Core API进行编程。这些API提供了用于数据处理的通用模块,如各种指定的transformations, joins, aggregations, windows, state等。在API中,这些处理的数据类型都是一个具体的实体类(class)。底层的Process Function与DataStream API集成在一起,可以仅对一些操作进行底层抽象。
  • Table API:是围绕着table的申明性DSL,可以被动态的改变(当其表示流时)。Table API遵循(扩展)关系模型:表有一个模式链接(类似与在关系数据库中的表),API也提供了一些类似的操作:select, project, join, group-by, aggregate等。Table API程序申明定义了怎么做是规范的,而不是明确指定应该是什么样子的。虽然Table API可以通过各种类型的用户定义的函数进行扩展,但它比Core API表达的更少,但使用起来更简洁(少写代码)。另外,Table API程序也会通过一个优化器,在执行之前应用优化规则。

可以在表和DataStream / DataSet之间进行无缝转换,允许程序混合使用Table API和DataStream 和DataSet API。

  • Flink提供的最高级抽象是SQL。这种抽象在语义和表现力方面与Table API类似,但是将程序表示为SQL查询表达式。在SQL抽象与Table API紧密地相互作用,另外,SQL查询可以在Table API中定义的表上执行。

程序和数据流(Programs and Dataflows)

Flink可以说是由流(streams)和转换(transformations)为基础构建的(请注意,Flink的DataSet API中使用的数据集也是内部的流 )。从概念上讲,流是数据记录(可能是永无止境的)流,而转换是将一个或多个流作为输入,并产生一个或多个输出流。

执行时,Flink程序被映射到由流和转换运算符组成的流式数据流。每个数据从一个或多个源(sources)开始,并在一个或者多个接收器(sinks)中结束。数据流类似于一个任意有向无环图(DAG)。尽管通过迭代构造允许特殊形式的循环,但是为了简单起见,我们姑且先忽视这种情况。

Flink 大数据 实时清理 flink dataflow_数据

程序中的转换与数据流中的操作符通常是一一对应的。然而,有时候,一个转换可能由多个转换操作符组成。

信号源(sources)和接收器(sinks)记录在流式连接器和批量连接器文档中。DataStream运算符和DataSet转换中记录了转换。

并行数据流(Parallel Dataflows)

Flink中的程序本质上是并行和分布的。在执行过程中,一个流有一个或者多个流分区,每个运算符有一个或者多个子任务。操作符子任务彼此独立,并且在不同的线程中执行,并且可能在不同的机器或容器上执行。

操作符子任务的数量是该特定操作符的并行度。流的并行性总是由生产它的操作符决定。同一个程序的不同运算符可能有不同的并行级别。

Flink 大数据 实时清理 flink dataflow_Flink 大数据 实时清理_02

流可以以一对一(One-to-one)或者重新分配(Redistributing)的模式在两个操作符之间传输:

  • One-to-one:保留了元素的分区和顺序,如上图中国source —>map。这意味着map运算符的subtask[1]将按照源运算符的subtask[1]所产生顺序相同。
  • Redistributing:如上图所示,ma和keyBy/window之间,以及keyBy/window和Sink之间重新分配流,将会改变流的愤怒。没个操作符子任务根据所选的转换将数据发送到不同的目标子任务。实例是keyBy()(其通过散列秘钥重新分区),broadcast(), or rebalance() (其随机的重新分区)。在重新分配 交换中,元素之间的排序只保存在每对发送和接收的子任务中(例如map()和subtask [2]的subtask[1]keyBy /window)。所以在这个例子里,每个关键字中的排序都被保留下来,但是并行性确实造成了,不同关键字汇总结果后顺序的非确定性。

有关配置和控制并行的细节可以在并行执行的文档中找到。

视图(Windows)

聚合事件(如:sum,count,etc)在流上的工作方式与批处理中的不同。例如,我们不能够去统计流中的所有元素,因为流一般是无限的(无界的)。相反,流中的一些aggregate操作,是由Windows控制的,例如:计算过去五分钟或者最后100个元素的总和。

Windows可以是由事件驱动的(例如,每30秒)或者数据驱动(例如每100个元素)。这可以用来区分不同类型的Windows,例如:tumbling windows (no overlap), sliding windows (with overlap), and session windows (punctuated by a gap of inactivity).

更多的窗口示例可以在这篇博客文章中找到。更多细节在窗口文件。

时间(Time)

当我们在流式编程中谈及时间时,可以引用不同的时间概念:

  • Event Time,是事件创建的时间,通常用时间戳表示。Flink通过时间戳分配器来访问事件时间戳。
  • Ingestion time,摄入时间(Ingestion Time)是事件进入Flink的时间,在源操作中每个记录都会获得源的当前时间作为时间戳,后续基于时间的操作(如: time window)会依赖这个时间戳
  • Processing Time,是指执行程序时对应的物理机的系统时间

有关如何处理时间的更多细节,请参阅活动时间文档。

有状态的操作(Stateful Operations)

尽管数据流中很多操作看起来像一次查看一个单独的事件,但是一些操作会跨越几个事件记下相关的的信息。何种操作被称为有状态的(stateful)。

这种有状态的操作,被保存在一种key/value的存储结构之中。状态与有状态操作符读取的流严格分区和分配。只有在keyed()函数之后才能访问key/value状态。并且仅限于与当前事件的键相关的值。流和状态的keys的匹配保证了所有状态更新都是本地操作,保证了一致性,所以不需要事务的开销。这种匹配还允许flink重新分配状态,并公开的调整分区。

有关更多信息,请参阅有关状态的文档。

检查点容错(Checkpoints for Fault Tolerance)

Flink使用流重播(stream replay)和检查点(checkpointing)的组合来实现容错。检查点与每个输入流中的特定点以及每个操作元的相应状态有关。流数据流可以从检查点恢复,同时保持一致性(正好处理一次处理语义),方法是恢复操作元的状态并从检查点重放事件。

检查点间隔是在执行恢复时间(需要重放的事件的数量)的情况下交换容错开销的手段。

容错内部的描述提供了有关Flink如何管理检查点和相关主题的更多信息。有关启用和配置检查点的详细信息位于检查点API文档中。

批处理流(Batch on Streaming)

Flink执行批处理程序作为流程的特殊情况,流程是有限的(有限元素)。A*数据集*在内部视为数据流。因此,上述概念同样适用于批处理程序,也适用于流式处理程序,但有一点例外:

  • 批处理程序的容错不使用检查点。通过完全重放流来恢复。这是可能的,因为投入是有限的。这将成本推向更高的复苏,但是使常规处理更便宜,因为它避免了检查点。
  • DataSet API中的有状态操作使用简化的内存/外核数据结构,而不是键/值索引。
  • DataSet API引入了特殊的同步(超级)迭代,这些迭代只能在有界的流上进行。有关详细信息,请查看迭代文档。