返回介绍

数据流编程模型

发布于 2025-05-02 18:19:12 字数 7001 浏览 0 评论 0 收藏 0

抽象层次

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

编程抽象级别

  • 最低级抽象只提供 有状态流 。它 通过 Process Function 嵌入到 DataStream API 中 。它允许用户自由处理来自一个或多个流的事件,并使用一致的容错 状态 。此外,用户可以注册事件时间和处理时间回调,允许程序实现复杂的计算。
  • 实际上,大多数应用程序不需要上述低级抽象,而是针对 Core API 编程, 如 DataStream API (有界/无界流)和 DataSet API (有界数据集)。这些流畅的 API 提供了用于数据处理的通用构建块,例如各种形式的用户指定的转换,连接,聚合,窗口,状态等。在这些 API 中处理的数据类型在相应的编程语言中表示为类。

    低级 Process Function 与 DataStream API 集成,因此只能对某些 算子操作进行低级抽象。该 数据集 API 提供的有限数据集的其他原语,如循环/迭代。

  • Table API 是为中心的声明性 DSL 表 ,其可被动态地改变的表(表示流时)。该 Table API 遵循(扩展)关系模型:表有一个模式连接(类似于在关系数据库中的表)和 API 提供可比的 算子操作,如选择,项目,连接,分组依据,聚合等 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 。

程序和数据流

Flink 程序的基本构建块是 转换 。(请注意,Flink 的 DataSet API 中使用的 DataSet 也是内部流 - 稍后会详细介绍。)从概念上讲, 流 是(可能永无止境的)数据记录流,而 转换 是将一个或多个流作为一个或多个流的 算子操作。输入,并产生一个或多个输出流。

执行时,Flink 程序映射到 流数据流 ,由 和转换 算子组成 。每个数据流都以一个或多个 开头,并以一个或多个 接收器 结束。数据流类似于任意有 向无环图 (DAG) 。尽管通过 迭代 结构允许特殊形式的循环 ,但为了简单起见,我们将在大多数情况下对此进行掩饰。

DataStream 程序及其数据流。

通常,程序中的转换与数据流中的 算子之间存在一对一的对应关系。但是,有时一个转换可能包含多个转换 算子。

源流和接收器记录在 流连接器批处理连接器 文档中。 DataStream 算子DataSet 转换 中记录了 转换

并行数据流

Flink 中的程序本质上是并行和分布式的。在执行期间, 流 具有一个或多个 流分区 ,并且每个 算子 具有一个或多个 算子子任务 。 算子子任务彼此独立,并且可以在不同的线程中执行,并且可能在不同的机器或容器上执行。

算子子任务的数量是该特定 算子的 并行 度。流的并行性始终是其生成 算子的并行性。同一程序的不同 算子可能具有不同的并行级别。

并行数据流

流可以 以一对一 (或 转发 )模式或以 重新分发 模式在两个算子之间传输数据:

  • 一对一 流(例如,在上图中的 Source 和 map() 算子之间)保存数据元的分区和排序。这意味着 map() 算子的 subtask [1] 将以与 Source 算子的 subtask [1]生成的顺序相同的顺序看到相同的数据元。
  • 重新分配 流(在上面的 map() 和 keyBy / window 之间,以及 keyBy / window 和 Sink 之间 )重新分配流。每个 算子子任务 将数据发送到不同的目标子任务,具体取决于所选的转换。实例是 keyBy() (其通过散列 Keys 重新分区), 广播() ,或 Rebalance () (其重新分区随机地)。在 重新分配 交换中,数据元之间的排序仅保存在每对发送和接收子任务中(例如, map()的 子任务[1] 和子任务[2] keyBy / window )。因此,在此示例中,保存了每个 Keys 内的排序,但并行性确实引入了关于不同 Keys 的聚合结果到达接收器的顺序的非确定性。

有关配置和控制并行性的详细信息,请参阅 并行执行 的文档。

窗口

聚合事件(例如,计数,总和)在流上的工作方式与批处理方式不同。例如,不可能计算流中的所有数据元,因为流通常是无限的(无界)。相反,流上的聚合(计数,总和等)由 窗口 限定,例如 “在最后 5 分钟内计数” 或 “最后 100 个数据元的总和” 。

Windows 可以是 时间驱动的 (例如:每 30 秒)或 数据驱动 (例如:每 100 个数据元)。一个典型地区分不同类型的窗口,例如 翻滚窗口 (没有重叠), 滑动窗口 (具有重叠)和 会话窗口 (由不活动的间隙打断)。

时间和计数 Windows

更多窗口示例可以在此 博客文章中 找到。更多详细信息在 窗口文档中

时间

当在流程序中引用时间(例如定义窗口)时,可以参考不同的时间概念:

  • 事件时间 是创建 事件的时间 。它通常由事件中的时间戳描述,例如由生产传感器或生产服务附加。Flink 通过 时间戳分配器 访问事件时间戳。
  • 摄取时间 是事件在源算子处输入 Flink 数据流的时间。
  • 处理时间 是执行基于时间的 算子操作的每个算子的本地时间。

事件时间,摄取时间和处理时间

有关如何处理时间的更多详细信息,请参阅 事件时间文档

有状态的 算子操作

虽然数据流中的许多 算子操作只是一次查看一个单独的 事件 (例如事件解析器),但某些 算子操作会记住多个事件(例如窗口算子)的信息。这些 算子操作称为 有状态

状态 算子操作的状态保持在可以被认为是嵌入式键/值存储的状态中。状态被分区并严格地与有状态算子读取的流一起分发。因此,只有在 keyBy() 函数之后才能在 被 Key 化的数据流 上访问键/值状态,并且限制为与当前事件的键相关联的值。对齐流和状态的 Keys 可确保所有状态更新都是本地 算子操作,从而保证一致性而无需事务开销。此对齐还允许 Flink 重新分配状态并透明地调整流分区。

状态和分区

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

容错检查点

Flink 使用 流重放检查点 的组合实现容错。检查点与每个输入流中的特定点以及每个算子的对应状态相关。通过恢复 算子的状态并从检查点重放事件,可以从检查点恢复流数据流,同时保持一致性 (恰好一次处理语义) 。

检查点间隔是在执行期间用恢复时间(需要重放的事件的数量)来折衷容错开销的手段。

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

流处理批处理

Flink 执行 批处理程序 作为流程序的特殊情况,其中流是有界的(有限数量的数据元)。一个 数据集 在内部视为数据流。因此,上述概念以相同的方式应用于批处理程序,并且它们适用于流程序,除了少数例外:

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

下一步

继续使用 Flink 的 Distributed Runtime 中 的基本概念。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。