姓名:闵聿宽
学号:16020188028
转自:https://blog.csdn.net/silentwolfyh/article/details/82865579
【嵌牛导读】数据流是我们如今在电子设计中常用的一种方法。下面介绍了一种新的FLINK-数据流编程模型
【嵌牛鼻子】FLINK数据流 编程模型
【嵌牛提问】FLINK-数据流编程模型具有怎样的特点和优势?
目录:
Flink提供了不同级别的抽象来开发流/批处理应用程序。
1) 低层级的抽象
最低层次的抽象仅仅提供有状态流。它通过Process函数嵌入到DataStream API中。它允许用户自由地处理来自一个或多个流的事件,并使用一致的容错状态。此外,用户可以注册事件时间和处理时间回调,允许程序实现复杂的计算。
2) 核心API
在实践中,大多数应用程序不需要上面描述的低级抽象,而是对核心API进行编程,比如DataStream API(有界或无界数据流)和DataSet API(有界数据集)。这些API提供了用于数据处理的通用构建块,比如由用户定义的多种形式的转换、连接、聚合、窗口、状态等。在这些api中处理的数据类型以类(class)的形式由各自的编程语言所表示。
低级流程函数与DataStream API集成,使得只对某些操作进行低级抽象成为可能。DataSet API为有界数据集提供了额外的原语,比如循环或迭代。
3) Table API
Table API是一个以表为中心的声明性DSL,其中表可以动态地改变(当表示流数据时)。表API遵循(扩展)关系模型:表有一个附加模式(类似于关系数据库表)和API提供了类似的操作,如select, project, join, group-by, aggregate 等。Table API 程序以声明的方式定义逻辑操作应该做什么而不是指定操作的代码看起来如何。虽然Table API可以通过各种用户定义函数进行扩展,但它的表达性不如核心API,但使用起来更简洁(编写的代码更少)。此外,Table API程序还可以在执行之前通过应用优化规则的优化器。可以无缝地在Table API和DataStream/DataSet API之间进行切换,允许程序将Table API和DataStream和DataSet API进行混合使用。
4) Sql层
Flink提供的最高级别抽象是SQL。这种抽象在语义和表示方面都类似于Table API,但将程序表示为SQL查询表达式。SQL抽象与表API密切交互,SQL查询可以在表API中定义的表上执行。
Flink程序的基本构建模块是streams 和 transformations 。(请注意,Flink的DataSet API中使用的数据集也是内部流——稍后将对此进行详细介绍。)从概念上讲,streams 是数据记录的(可能是无限的)流,而transformations是将一个或多个流作为输入并产生一个或多个输出流的操作。
执行时,Flink程序被映射到流数据流,由streams 和 transformations 操作符组成。每个数据流以一个或多个sources开始,以一个或多个sinks结束。数据流类似于任意有向无环图(DAGs)。虽然通过迭代构造允许特殊形式的循环,但为了简单起见,我们将在大多数情况下忽略这一点。
通常在程序中的transformations和数据流中的操作之间是一对一的对应关系。然而,有时一个transformations可能包含多个transformations操作。
在streming连接器和批处理连接器文档中记录了Sources 和 sinks。在DataStream运算和数据集transformations中记录了transformations。
Flink中的程序本质上是并行的和分布式的。在执行期间,流有一个或多个流分区,每个operator 有一个或多个operator subtasks(操作子任务)。operator subtasks相互独立,在不同的线程中执行,可能在不同的机器或容器上执行。
operator subtasks的数量是特定运算符的并行度。一个流的并行性总是它的生产操作符的并行性。同一程序的不同运算符可能具有不同级别的并行性。
流可以在两个操作符之间以一对一(或转发)模式传输数据,也可以在重新分配模式中传输数据:
One-to-one 流(例如上图中Source和map()运算符之间的流)保持元素的分区和顺序。这意味着map()操作符的subtask[1]将看到与源操作符的subtask[1]生成的元素相同的顺序。
Redistributing 流(如上面的map()和keyBy/window之间,以及keyBy/window和Sink之间)改变流的分区。每个操作符子任务根据所选的转换将数据发送到不同的目标子任务。例如keyBy()(通过散列键来重新分区)、broadcast()或balanced()(随机重新分区)。在重分发交换中,元素之间的顺序只保留在每一对发送和接收子任务中(例如map()的子任务[1]和keyBy/window的子任务[2])。因此,在本例中,每个键中的顺序都是保留的,但是并行性确实引入了关于不同键的聚合结果到达sink的顺序的不确定性。
聚合事件(例如计数、求和)在流上的工作方式与批处理不同。例如,不可能计算流中的所有元素,因为流通常是无限的(无界的)。相反,流上的聚合(计数、求和等)是由窗口限定作用域的,例如“过去5分钟的计数”或“最后100个元素的总和”。
Windows可以是时间驱动(示例:每30秒)或数据驱动(示例:每100个元素)。一个典型的方法是区分不同类型的窗口,比如翻滚窗户(没有重叠)、滑动窗口(有重叠)和会话窗口(中间有一个不活跃的间隙)。
当提到流程序中的时间(例如定义窗口)时,可以指不同的时间概念:
事件时间 : 是创建事件的时间。它通常由事件中的时间戳描述,例如由生产传感器或生产服务附加。Flink通过时间戳转让者访问事件时间戳。
摄入时间 : 在source操作符中一个事件进入Flink数据流的时间。
处理时间 : 是执行基于时间的操作的每个操作符的本地时间。
虽然一个数据流中有许多操作但只看作一个单独的事件(例如事件解析器),但是一些操作记住了跨多个事件的信息(例如窗口操作符)。这些操作称为有状态操作。
有状态操作的状态被维护在可以认为是嵌入式键/值存储中。状态与有状态操作符读取的流一起被严格地分区和分布。因此,在keyBy()函数之后,只能在键控流上访问键/值状态,并且只能访问与当前事件的键相关联的值。对齐流和状态的键确保所有的状态更新都是本地操作,保证一致性而不增加事务开销。这种对齐还允许Flink透明地重新分配状态和调整流分区。
(EventTime是信息自带的时间,再进入消息队列,IngestionTime是进入Flink的时间,Processing是进入Operator的时间)
Flink通过流回放和检查点的组合实现了容错。检查点与每个输入流中的特定点以及每个操作符的对应状态相关。通过恢复操作符的状态并从检查点重新播放事件,流数据流可以在检查点恢复,同时保持一致性(准确地说是一次处理语义)。
检查点间隔是在执行期间用恢复时间(需要重放的事件数量)来权衡容错开销的一种方法。
Flink执行批处理程序作为流程序的特殊情况,其中流是有界的(有限的元素数量)。数据集在内部被视为数据流。因此,上述概念同样适用于批处理程序,也适用于流程序,但有少数例外:
批处理程序的容错不使用检查点。恢复通过完全重放流来实现。这是可能的,因为输入是有界的。这将使成本更多地用于恢复,但使常规处理更便宜,因为它避免了检查点。
数据集API中的有状态操作使用简化的内存/核心外数据结构,而不是键/值索引。
DataSet API引入了特殊的synchronized(基于超步的)迭代,这只能在有界的流上实现。有关详细信息,请查看迭代文档。