Apache Flink 非常适用于流分析应用程序,因为它支持事件时间语义,确保只处理一次,以及同时实现了高吞吐量和低延迟。因为这些特性,Flink 能够近实时对大量的输入数据计算出一个确定和精确的结果,并且在发生故障的时候提供一次性语义。
Flink 的核心流处理 API,DataStream API,非常具有表现力,并且为许多常见操作提供了原语。在其他特性中,它提供了高度可定制的窗口逻辑,不同表现特征下的不同状态原语,注册和响应定时器的钩子,以及高效的异步请求外部系统的工具。另一方面,许多流分析应用遵循相似的模式,并不需要 DataStream API 提供的表现力级别。他们可以使用领域特定的语言来使用更自然和简洁的方式表达。众所周知,SQL 是数据分析的事实标准。对于流分析,SQL 可以让更多的人在数据流的特定应用中花费更少的时间。然而,目前还没有开源的流处理器提供令人满意的 SQL 支持。
为什么流中的 SQL 很重要
SQL 是数据分析使用最广泛的语言,有很多原因:
- SQL 是声明式的:你指定你想要的东西,而不是如何去计算;
- SQL 可以进行有效的优化:优化器计估算有效的计划来计算结果;
- SQL 可以进行有效的评估:处理引擎准确的知道计算内容,以及如何有效的执行;
- 最后,所有人都知道的,许多工具都理解 SQL。
因此,使用 SQL 处理和分析数据流,可以为更多人提供流处理技术。此外,因为 SQL 的声明性质和潜在的自动优化,它可以大大减少定义高效流分析应用的时间和精力。
但是,SQL(以及关系数据模型和代数)并不是为流数据设计的。关系是(多)集合而不是无限序列的元组。当执行 SQL 查询时,传统数据库系统和查询引擎读取和处理完整的可用数据集,并产生固定大小的结果。相比之下,数据流持续提供新的记录,使数据随着时间到达。因此,流查询需要不断的处理到达的数据,从来都不是“完整的”。
话虽如此,使用 SQL 处理流并不是不可能的。一些关系型数据库系统维护了物化视图,类似于在流数据中评估 SQL 查询。物化视图被定义为一个 SQL 查询,就像常规(虚拟)视图一样。但是,查询的结果实际上被保存(或者是物化)在内存或硬盘中,这样视图在查询时不需要实时计算。为了防止物化视图的数据过时,数据库系统需要在其基础关系(定义的 SQL 查询引用的表)被修改时更新更新视图。如果我们将视图的基础关系修改视作修改流(或者是更改日志流),物化视图的维护和流中的 SQL 的关系就变得很明确了。
Flink 的关系 API:Table API 和 SQL
从 1.1.0 版本(2016 年 8 月发布)以来,Flink 提供了两个语义相当的关系 API,语言内嵌的 Table API(用于 Java 和 Scala)以及标准 SQL。这两种 API 被设计用于在线流和遗留的批处理数据 API 的统一,这意味着无论输入是静态批处理数据还是流数据,查询产生完全相同的结果。
统一流和批处理的 API 非常重要。首先,用户只需要学习一个 API 来处理静态和流数据。此外,可以使用同样的查询来分析批处理和流数据,这样可以在同一个查询里面同时分析历史和在线数据。在目前的状况下,我们尚未完全实现批处理和流式语义的统一,但社区在这个目标上取得了很大的进展。
下面的代码片段展示了两个等效的 Table API 和 SQL 查询,用来在温度传感器测量数据流中计算一个简单的窗口聚合。SQL 查询的语法基于 Apache Calcite 的分组窗口函数样式,并将在 Flink 1.3.0 版本中得到支持。