Flink提供了三种主要的sdk/API来编写程序:Table API/SQL、DataStream API和DataSet API。我们认为这个API太多了,建议弃用DataSet API,而使用Table API/SQL和DataStream API。当然,这说起来容易做起来难,所以在下面,我们将概述为什么我们认为太多的api对项目和社区有害。然后,我们将描述如何增强Table API/SQL和DataStream API以包含DataSet API的功能。
在本FLIP中,我们将不描述如何增强Table API/SQL和DataStream的所有技术细节。目标是在弃用DataSet API的想法上达成共识。必须有后续的flip来描述我们所维护的api的必要更改。
这三种api在项目的生命周期中被有机地开发出来,最初是为特定的用例设计的。DataSet API是Flink最古老的API,支持有界数据的批处理执行。有些人可能不记得了,但Flink最初是一个批处理程序。在早期,社区意识到其基于管道的体系结构非常适合流处理,这就产生了DataStream API。后者是为无界的流用例开发的,具有处理状态、事件时间和窗口的特殊设施。随着Flink在分析领域的流行,Table API/SQL被引入,以提供支持批处理和流处理的高级关系API。
对于下面的讨论,理解每个API的区别特性将会很有帮助。
DataSet API:
- 只支持有界源
- 没有为事件时间/窗口提供特殊支持
- 全有或全无输出:作业要么产生数据,要么不产生数据
- 用于大规模批处理作业的细粒度的、基于区域的故障转移。执行中某一部分的失败并不一定需要重新启动整个拓扑
- 高效的数据库式操作符:散列连接、合并连接、对使用输入数据有界知识的聚合进行排序/分组
DataStream数据API:
- 源可以是有界的,也可以是无界的
- 对事件时间和窗口的特殊支持
- 基于水印或检查点的“增量”输出
- 故障恢复检查点,这意味着在一个算子失败的情况下重新启动整个拓扑
- 你可以执行有界程序,但效率不高:
- 悲观的假设,没有结束标识,“你不知道接下来会发生什么”
- 对于聚合,需要将所有键保存在一个“哈希映射”中
- 对于事件时间处理,我们需要保持多个“窗口”打开
- 没有基于阻塞、持久洗牌的细粒度恢复
Table/SQL API:
- 有界和无界源
- 一个声明性API,以及SQL
- 数据有一个预先知道的结构,因此允许额外的优化,例如,分组时只反序列化记录中需要的部分,完全处理二进制数据,以及整个查询优化
- 同样的查询/程序可用于有界和无界源
- 流和批处理的高效执行,这意味着对于有界执行,我们可以使用DataSet API使用的执行模型,对于流用例,使用DataStrem API的执行模型。这对用户来说是透明的。
- 没有低级算子API,即没有计时器、状态
- 不控制生成的执行DAG→查询优化器会阻止保存点兼容性
自然会出现这样的问题:“为什么社区最初不扩展DataSet API来处理无界/流式的工作负载,而是添加DataStream API ?”简单的回答是,我们当时没有花时间去思考一个API如何同时服务于两个用例。
为什么有太多的api不好呢?我们看到当前局势存在两个主要问题:
当需要物理API时,重用Flink应用程序的无界处理和有界处理是不现实的:
我们认为,对于用户来说,编写一个管道来分析流数据/无界数据,然后希望重用相同的代码来处理有界数据/批处理数据是很常见的。例如,当你想处理S3的历史数据时,实时管道会从Kafka读取数据。理论上,可以对有界源使用DataStream API,但无法获得有效的执行或容错行为。如果出现故障,整个管道必须重新启动。这与DataSet API的执行模型不同,在该模型中,只需要重启单个操作或连接的子图,因为我们可以保留操作的中间结果。
如果您事先知道所有的输入,那么使用事件时间语义就会容易得多。水印可以总是“完美”的,因为没有早期或晚期数据,我们用于批处理式执行的算法和数据结构可以考虑到这一点。
DataSet和DataStream API有不同的可用连接器集,因为它们使用不同的API来定义源和接收。例如,你不能用批处理类型的作业从Kafka读取一个有界的区间。
最后,我们认为DataStream API的事件时间/窗口特性对于批处理也很有用。例如,当您想要处理时间序列数据时。目前,您可以使用DataStream API并处理低于标准的执行行为,或者使用DataSet API并使用排序/分组手动实现窗口。
用户必须提前在api之间做出选择:
这增加了认知负荷,使Flink对新用户来说更不容易接近。如果他们一开始就做出了错误的选择,那么如果没有大量的时间投资,他们将无法在以后转换。
另一个方面是,想要采用Flink的大型组织可能会因为不得不对工程师进行两种不同api及其潜在语义差异的培训而受挫,例如什么是延迟,什么是事件时间,以及它是否与批处理相关等。
修改建议我们建议弃用DataSet API,而使用Table API/SQL和DataStream API。为了实现这一点,我们需要增强Table API/SQL和DataStream API,使其成为以前使用DataSet API的情况下可用的替代品。我们将在这里概述所需的更改,但将更具体的计划推迟到后续的FLIPs。对于这个提议,我们只是想让社区就弃用DataSet API的总体想法达成共识,并概述其他API所需的变化。
对“幸存者”api的更改Table/SQL API:
- 必须容易在代码中内联定义源/接收器。这在FLIP-129:重构描述符API中进行了介绍,以在表API中注册连接器。
- 我们应该在Table上有易于使用的“命令式”操作,也就是应该有“人体工程学”map/filter/flatMap。这些操作应该是面向行/记录的,而不是常规Table API操作的面向列的性质。这样用户就不必学习表达式DSL语法来编写操作。
- 此外,我们还希望弃用/删除遗留表API批处理计划器以及批执行环境,因为它们与DataSet API互操作
DataStream数据API:
- 我们需要一个适用于有界和无界源的源API。这在FLIP-27:重构源接口中有涉及。
- 我们需要一个适用于无界和有界源的接收器API。这是由FLIP-143:统一汇聚API覆盖的
- 我们需要定义一组通用的执行语义,用于批处理和流执行,这包括重新考虑DataStream API中的一些决策,使它们在一个统一的世界中工作。这在FLIP-134: DataStream API的批处理执行中得到了介绍
- 当拓扑有界时,我们需要为DataStream程序使用高效的批处理式执行。这在FLIP-140中有涉及:为有界键控流引入批处理样式的执行
- 特别是对于机器学习用例,我们需要对迭代计算的健壮支持。目前DataStream API中对迭代的支持应该被认为是实验性的,它不像DataSet API那样支持动态终止标准。然而,我认为我们需要在后续FLIP中解决这个问题。
我们目前还没有明确的指南来指导用户应该使用哪种API。我们需要就建议达成一致,然后在文件和一般营销中积极推广它们。这可能会以决策树或其他图形化决策工具/应用的形式出现。
我们目前的想法总结如下:
- 如果你有一个模式,没有“低级”操作→SQL/Table
- 如果你需要显式控制执行图、操作、操作中的状态→使用DataStream API
也就是说,应该可以在Table API和DataStream API之间自由转换。FLIP-136:改善DataStream和Table API之间的互操作性使这些工作更加具体。
兼容性、弃用和迁移计划DataSet API应该在文档和代码中标记为已弃用,并描述项目的未来方向。在即将到来的版本中。
- 用户有足够的时间将现有的用例迁移到其他API,
- 我们确信剩余的API足以覆盖DataSet API的用例,我们就应该删除DataSet API。
这取决于上面提到的后续FLIPs,所以当满足上面概述的条件时,这个FLIPs可以被认为是完整的。
重要的是要记住,我们不能简单地将DataSet API从一个版本移到下一个版本。这将是一个较长的过程,我们需要确保DataSet API的现有用户能够迁移并进行迁移。有些公司在DataSet API上投入了大量资金,但他们不能把它们抛在后面。
拒绝选择我们认为,如果可用api的重叠像DataStream和DataSet api那样明显,那么除了减少可用api的数量别无选择。理论上我们可以弃用DataStream API,支持DataSet API,但我们认为DataStream API是更广泛使用的API,而且目前它的功能也更齐全(请参阅事件时间处理和窗口)。这也与“批处理是流的一部分,批处理是流处理的严格子集”的思想产生了很好的共鸣。
关注gzh HEY DATA 一起交流更多。