抽象层次
Flink 为开发流式应用和批式应用设计了不同的抽象层次
状态化的流
抽象层次的最底层是状态化的流, 它通过 ProcessFunction 嵌入到 DataStream API 中, 允许用户自由地处理来自一个或多个流的事件 (event) 以及使用一致的容错状态
此外, 用户可以注册事件时间并处理时间回调(callback), 这使得程序可以处理更复杂的计算
核心 API
大多数情况下用户不直接在上面描述的这种低的抽象层面上编程, 取而代之的是使用所谓的核心 API, 包括可以处理 Unbounded 和 Bounded 流的 DataStream API 和处理 Bounded 数据集的 DataSet API
这些 API 提供了数据处理通用的构建手段, 包括用户定义的各种类型的转换(transformation), 连接(join), 聚合(aggregation),Windows 和状态等, 经由这些 API 处理的数据类型被表示为所使用的编程语言中的类(class)
低抽象层次的处理函数被整合到 DataStream API 中以实现对特定操作的精细处理
DataSet API 为 Bounded 数据集提供了额外的原语 (primitive), 包括循环(loop) 和迭代(iteration)
Table API
Table API 是处理表的一种 DSL, 表代表着流, 而 Table API 可以动态地修改表
Table API 使用扩展的关系模型, 表关联着模式, 就像关系数据库中的表一样, API 提供了对应的操作, 包括 select,project,join,group-by 和 aggregate 等等
Table API 写成的程序使用声明式的语法指示操作应该在逻辑上怎么运行, 而不是具体地指定操作应该怎么应用
虽然 Table API 可以通过用户定义的函数进行拓展, 但它的表达能力还是比不上核心 API, 不过 Table API 的优点在于使用起来非常简洁, 此外, Table API 写成的程序在执行前会被特定的优化器优化
Table API 和 DataStream/DataSet API 可以混用, 这是因为, 由于对偶性, 表和 DataStream/DataSet 可以无缝地转换
SQL
Flink 提供的最高抽象层次是 SQL, 这层抽象在语义和表现上都类似于 Table API, 但是语法上表现为 SQL 语句
SQL 抽象和 Table API 密切关联, 并且 SQL 查询可以在 Table API 定义的表上进行
程序与数据流
Flink 程序的基本块包括流 (stream) 和转换(transformation), 即使是使用 DataSet API 操作的 DataSet, 在内部也表现为流
从概念上说, 流 (stream) 是源源不断的数据记录流 (flow), 转换(transformation) 是接收若干个流为输入, 产生若干个流为输出的操作, 程序中的转换通常和流中的转换操作一一对应, 但也可能对应多个转换操作
Flink 程序在执行时被映射为 Streaming Dataflow, 包括流 (stream) 和转换操作 (transformation operator), 每个数据流开始于若干个源(source) 并结束于若干个汇(sink)
数据流表现为任意有向无环图(DAG), 虽然通过迭代结构可以产生一些环, 但是简单起见我们可以不太在意它
并行的数据流
Flink 的程序自动地是并行的和分布的, 在执行的时候, 每个流被分成若干个流的部分(stream partition), 每个操作对应着若干个子操作(operator subtask)
子操作之间是相互独立的, 运行在不同的线程上, 这意味着有可能运行在不同的机器或容器上
特定操作的子操作数量代表了它的并行度, 流的并行度由它的 Producing Operator 决定, 同一个程序的不同操作可能有不同的并行度
流传输数据有两种模式, 一对一的模式 重新分配的模式
一对一的流, 例如上图中
Source
到
map()
的过程, 保持了元素的分组和顺序, 也就是说 map()[1] 看到的元素包括顺序都和 Source[1] 一样
重新分配的流, 例如上图中 map() 到 keyBy/window() 的过程和 keyBy/window() 到 sink() 的过程, 会改变流的划分, 每个操作的子任务, 根据选定的转换, 向不对应的下一个操作的子任务发送数据, 例如
keyBy()
使用键的哈希值重新分配, boardcast() 做全频广播, rebalance() 做随机重新分配
窗口(Windows)
事件的聚合, 例如 count 和 sum, 在流处理和批处理上是不一样的, 比如说, 我们没办法统计流上的所有的元素, 在流上做聚合的一般方法是通过 windows, 例如统计最近五分钟的元素或计算最近一百个元素的和
Windows 可以是时间驱动或数据驱动的, 类似于上面的两个例子
Windows 有不同的类型, 包括滚动窗口 (tumbling windows), 滑动窗口(sliding windows) 和会话窗口(session windows
时间(Time)
流式程序中的时间通常包括以下几种概念
事件时间 (event time) 指的是一个事件被创建的时间, 通常由事件中的时间戳 (timestamp) 描述, 例如传感器或服务器, Flink 通过 Timestamp Assigners 获取时间戳
摄入时间 (ingestion time) 指的是事件从 Flink 数据流中的源进入的时间
处理时间 (processing time) 指的是执行具体操作的机器的本地时间
状态化的操作
数据流中的操作有些对于每次处理每个事件都是单独的, 不过也有些需要记住多个事件之间的信息, 例如在 windows 操作中, 这些操作被称为状态化的
状态化的操作的状态由嵌入式的键值对维护, 状态被划分并分布到对应的流中, 被状态化操作读取, 因此表示为键值对的状态仅能在键化 (keyed) 的流中获取, 首先经过一个 keyBy 函数, 然后通过当前事件的键获取
流状态和键的对齐使得状态更新都是本地操作, 这就使得一致性无需额外的事务开销, 还能支持 Flink 透明地重新分配和调整划分
容错机制中的检查点
Flink 使用流重放 (stream replay) 和检查点 (checkpointing) 技术的组合来支持容错
检查点与每个输入流中的特定点和每个操作对应的状态有关, 可以通过回滚操作的状态并从检查点重放事件来恢复流式数据流, 同时维持一致性, 保证 Excatly-once 处理的语义
容错点的间隔是对执行中容错的保障和等待恢复时间的权衡, 等待恢复的时间主要取决于需要重放的事件数
流上的批处理
Flink 将批处理程序作为流处理程序的特例来执行, DataSet 在内部作为流式数据处理, 因此上面提到的技术也以类似的形式应用到批处理上, 大同小异, 主要的不同点如下
批处理的容错机制没有检查点, 从错误中恢复是通过完全重放整个流来实现的, 虽然这使得恢复的时间成本增大, 但是因为不需处理检查点, 可以加速正常处理流程
DataSet API 中的状态化操作使用简化的内存 / 外核数据结构取代键 / 值索引
DataSet API 提供了基于超步 (superstep) 的专门的同步的迭代过程, 这种过程只能在 Bounded 的流上执行
来源: https://www.cnblogs.com/wander4096/p/8507384.html