论文地址: 一个SQL来管理所有操作:一种高效的、语法上惯用的流和表管理方法 Begoli等人,SIGMOD’19

在数据处理方面,似乎所有的道路最终都会回到SQL!今天的论文选择是由来自Apache Beam、Apache Calcite和Apache Flink项目的一组专家撰写的,他们概述了为流式处理构建SQL接口的经验。最终的结果是在国际SQL标准化组织的支持下,对SQL标准本身进行了一系列的扩展。

本文的主题是,在开发支持真实流式处理用例的大型开源框架的经验支持下,SQL语言和关系模型能够非常有效地处理流式处理数据。
这里提出的许多想法已经被Apache Beam、方解石和Flink以某种形式实现,作为其中的一个选项。流式SQL接口已经被阿里巴巴、华为、Lyft、Uber等公司采用,并向作者提供了以下反馈,说明他们做出此选择的原因:

  • 与非声明性流处理api相比,开发和采用成本显著降低
  • 与非标准化查询语言相比,熟悉标准SQL可以简化采用过程
  • 由于事件时间语义的缘故,常见的流处理任务(如窗口聚合和连接)可以很容易地表示和有效地执行
  • 在应用程序逻辑错误或服务中断的情况下,记录的数据流可以由处理实时数据流的同一查询重新处理。

基本原则
最大的目标是将表和流的抽象统一到一个通用框架中:
综合起来,表和流涵盖了业务运营的关键范围,从历史数据支持的战略决策到交互分析中使用的近实时数据……我们相信,基于我们的经验和近20年来对流式SQL扩展的研究,以一致的方式使用相同的SQL语义是统一这两种数据模式的高效而优雅的方法…
正如作者所指出的,多年来在这一领域已经有了大量的前期工作,本文提出的建议在很大程度上借鉴了这一成果。在锐利的结尾,他们是基于经验教训工作阿帕奇燧石,梁,方解石。
流式处理给传统关系视图增加了时间概念。注意,消费者在多个查询中看到的可变数据库表已经是一个时变关系(TVR)。只是对于任何一个查询,结果总是在一个时间点上显示关系。
时变关系正是这个名称所暗示的:其内容可能随时间而变化的关系……先前工作中所述但未充分利用的关键见解是,流和表是一个语义对象的两种表示。
根据定义,TVR支持整个关系运算符套件,即使在涉及时变关系数据的场景中也是如此。所以,提案的第一部分基本上是禁止操作!我们需要tvr,而这正是关系已经存在的原因,所以让我们使用它们-并明确说明SQL在tvr上操作。
不过,我们确实需要一些扩展来处理事件时间的概念。特别是,我们需要注意将事件时间与处理时间(稍后可能是任意时间)分开。我们还需要理解,事件不一定按照事件时间顺序呈现以供处理。

我们提出通过两个概念来支持事件时间语义:显式事件时间戳和水印。同时,它们允许在不消耗无限资源的情况下有效地表示和执行正确的事件时间计算,例如分组到事件时间的间隔(或窗口)。
该WaterMarking模型使用追溯到MillwheelGoogle云数据流,从那里到Beam和Flink。对于处理时间中的每个时刻,水印指定在处理时间的该点输入被认为是完整的事件时间戳。
这个难题的第三部分是提供一些对关系如何呈现以及行何时具体化的控制。例如:一个查询的输出是否应该立即改变以反映任何新的输入(通常是过多的),或者我们只是想在窗口的末尾看到成批的更新?

例子
来自NEXmark流查询基准的查询7监视拍卖中的最高价格项。每十分钟,它返回最近十分钟内的最高出价和相关项ID。
下面是使用建议的SQL扩展来表示的内容。与其用冗长的文字描述正在发生的事情,我选择只注释查询本身。希望你能明白要点…

streaming-sql-annotated-query.jpeg

考虑到以下事件

streaming-sql-events.jpeg

然后,在8:21计算的查询将产生以下TVR:

streaming-sql-query-1.jpeg

而8点13分的评估结果会有所不同:

streaming-sql-query-2.jpeg

请注意,正如当前所表示的,查询返回时间点结果,但是如果我们想更改,可以使用具体化延迟扩展。例如,选择。。。在水印之后发出;仅当水印经过窗口的末端时才发出行。

所以在8:16我们会看到
streaming-sql-after-watermark.jpeg

在 8:21:
streaming-sql-after-watermark-2.jpeg

如果我们想看到基于时间窗口的行而不考虑水印,但只获取定期聚合的快照,我们可以使用SELECT。。。延迟后发出流(这里的流表示我们也需要流结果)。

streaming-sql-periodic.jpeg
SQL扩展
希望这给了你一个很好的味道。目前,该建议包含标准SQL的7个扩展:

  • 带水印的事件时间列:关系中的事件时间列是带关联水印的TIMESTAMP类型的可分辨列。水印由系统维护。
  • 按事件时间戳分组:当GROUPBY子句在事件时间列上分组时,只包括键小于该列水印的组
  • 事件时间窗口功能:从Tumble和Hop开始,Tumble和Hop接受一个关系和事件时间列描述符,并返回一个带有其他事件时间列作为输出的关系。翻滚产生等间距不相交的窗口,跳跃产生等大小的滑动窗口。
  • 流物化:以时变关系的形式发出流结果,表示对查询的经典结果的更改。其他列指示行是否是前一行的缩回、行的更改日志处理时间偏移量以及相对于同一事件时间分组的其他更改的序列号。
  • 物化延迟:当查询具有EMIT AFTER WATERMARK修饰符时,仅物化结果中的完整行
  • 周期物化:当查询在延迟d行物化为周期d后发出时,而不是连续。
  • 组合物化延迟:查询在延迟d之后发出,水印行在周期d物化之后以及完成时发出。

Hop例子

streaming-sql-hop.jpeg

streaming-sql-hop-results.jpeg

Emit流例子

streaming-sql-emit-stream.jpeg

一路上吸取的教训
论文的第5节列出了从阿帕奇方解石、燧石和横梁中汲取的经验教训,这些经验教训为设计提供了依据。我没有足够的空间来覆盖它们,但作为一种味道,这里有两个吸引了我的注意:
由于事件时间戳只是常规属性,可以在普通表达式中引用,因此表达式结果可能不会与水印保持对齐,这需要在查询规划过程中加以考虑。
用户发现很难对查询中事件时间的最佳使用进行推理,这会导致代价高昂的执行计划,而且语义不理想。

今后的工作
对我来说,这项工作令人印象深刻的一点是,用这么少的时间就能取得多大的成就。不过(§8)很快就会看到,在完成这项工作之前,扩展集还需要增加一些。
例如,吸引我注意的一个领域是SQL标准定义,即SQL查询中的时间在查询时是固定的(可以是到当前时间,也可以是到指定的固定时间,使用截止系统时间)。这意味着您还不能在流的尾部表示视图(您可以在谓词中使用诸如CURRENT_TIME-INTERVAL’1’HOUR之类的表达式,但是CURRENT_TIME在执行查询时会采用一个固定值)。同样,在特定时间点(例如,事件时间)使用来自时态表的属性丰富TVR需要额外的支持。