推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

腾讯基于 Flink SQL 的功能扩展与深度优化实践

   2023-08-18 网络整理佚名1870
核心提示:语言已经存在了很长时间了,它有自己的一套标准,主要面向数据分析人员。标准,数据分析人员就可以在不同的平台和计算引擎之间进行切换。腾讯实时计算平台目前的工作,基于这两个字段可以把数据归纳到一个确定的窗口。合并在一起去翻译,这样可以减少中间环节代码的产生,从而提高性能。

一、背景及现状

1、三种模式分析

目前创建 Flink 作业的方式有 JAR 模式、 模式、SQL 模式三种。 提交作业的不同方式针对不同的人群。

■ 罐子模式

Jar模式基于/API开发,主要针对底层开发者。

■ 画布模式

所谓模式,一般会提供可视化的拖拽界面,让用户通过界面进行拖拽操作,完成Flink作业的编辑。 它针对的是一些新手用户。

■ SQL 模式

SQL语言已经存在很长时间了,它有自己的一套标准,主要针对数据分析师。 只要遵循现有的SQL标准,数据分析师就可以在不同的平台和计算引擎之间切换。

2、腾讯实时计算平台目前所做的工作

■ 扩展语法

定义table-语法是为了帮助用户实现基于窗口的流Join、交集和合并操作。 此外,它还实现了自己的流和维表Join语法。

■ 新功能

添加了一些新功能,包括两种新类型:(增量窗口)和(增强窗口)。 实现了Field和Table的解耦。 很多情况下,Field不能用Table字段来定义。 例如Table是子查询或者某个时间字段通过函数转换。 我想使用这些中间生成的时间字段作为字段。 是不可能的。 我们目前的解决方案是允许用户选择物理表中的任意时间字段来定义时间属性并输出。

■ 性能调整

■ 加入

流表和维表的join存在数据冷启动问题。 如果Flink任务在启动时加载大量外部数据,很容易造成背压。 所有数据都可以在启动时通过 State API 等方式预加载到内存中。 但这个解决方案有一个问题。 将维表数据全部加载到其中会导致大量的内存消耗。 因此,我们的解决方案是在维度表的定义中指定一条信息。 当流和维表连接时,会根据该信息加载维表中分片对应的数据。 同时翻译执行计划时会得到流表。 确保流中的数据和维度表中的数据将基于相同的信息进行连接的信息。 这种方法可以大大减少全维表数据预加载带来的内存消耗问题。

2. 窗口功能扩展

腾讯实时计算平台在现有的Flink SQL语法基础上做了一些扩展,定义了两种新的类型。

1.新窗口操作

现有要求如下。 需要在一定的时间窗口内对两个流进行Join操作或者交差操作。

使用Flink SQL基于一定的方法进行双流Join,现有的方案有两种。 第一种方案是先join,然后group by,第二种方案是join。 首先我们分析一下第一种方案是否能够满足需求。

■ 1.1 先加入再打开窗口

先加入再打开窗口的逻辑如上图所示。 根据逻辑执行计划可以看出Join节点在该节点下,所以会先进行流之间的join,等join完成后再进行join。

从图右侧的流程图也可以看出,首先将两边的流为一,然后根据Join Key进行Keyby操作,从而保证数据与两个流中相同的Join Key可以发送到同一个任务。 左流会将数据存储在自己的状态中,同时会去右流的状态进行Match,如果能够匹配,则将Match后的结果输出到下游。 该解决方案有两个问题:

状态无法清除:在Join窗口打开之前,Join中没有任何信息。 即使下游触发并完成计算,也无法清除上游两个流的Join状态。 最多只能使用基于TTL的方法。 清理。

语义无法满足需求:原来的需求是根据两个流中相同的时间窗口对数据进行切片,然后进行join,但是目前的方案无法满足这个需求,因为它是先进行join,再使用join后的数据然后打开窗口,这种方法无法保证两个流中参与Join的数据是基于同一个窗口的。

■ 1.2 加入

相比之前的写法,Join的好处是不存在状态无法清理的问题,因为扫描左右流的数据时,可以基于某个窗口,状态可以窗口时间过后将被清除。

不过相比第一种方案,这种方案的数据精度可能会差一点,因为它对窗口的划分并不是基于某个窗口,而是由数据驱动的,即当前数据可以加入另一个流以上数据范围以当前数据为准。 这种窗口划分的语义和我们的需求还是有一定差距的。

想象一下,有两个速率不一致的流,左流可以加入的右流的数据范围受到低和上两个边界的限制。 在这样严格的范围约束下,右流中总会有一些有效数据在时间上落在窗口[左+下,左+上]之外,计算不够准确。 因此,最好按照窗口对齐方式来划分时间窗口,使得两个流中的相同数据落在同一个时间窗口内。

■ 1.3 表-

腾讯对Table-语法进行了扩展,可以满足“在一定时间窗口内对两个流进行连接操作或交差操作”的需求。 SQL 2016 标准中对此语法有描述,并且该语法已经存在于 .23 中。

Table-语法可以清楚地描述其整个语义,From子句包含了定义所需的所有信息,包括Table、Field、Size等。

从上图中的逻辑规划可以看出,该语法在其中添加了一个名为 Scan 的节点。 另外,节点(输出节点)还有两个字段叫做and,根据这两个字段,可以将数据汇总到某个窗口中。 基于以上原则,Table-语法可以做以下事情:

■ 1.4 实施细节

下面简单介绍一下我们实现Table-语法的一些细节。

原来的逻辑计划翻译方式是先基于,然后翻译为Table-,最后翻译为Limit子句。 整个过程会多次存储状态,这对于性能来说会是一个比较大的消耗。 因此,进行了如下优化,将多个翻译合并在一起,可以减少中间代码的生成,提高性能。

可以看到Table-的语法:

SELECT * FROM TABLE(TUMBLE(TABLE <data>, DEscriptOR(), <size> [, <offset>]))

table不仅可以是表,还可以是子查询。 因此,如果定义Field时将时间属性绑定到Table上,而Table又恰好是子查询,此时就无法满足我们的需求。 因此,我们在实现语法时,将时间属性字段与Table解耦。 反之,用户使用物理表中的任意时间字段作为时间属性,从而生成。

使用逻辑与其他语法相同,两个流的所有Input Task的最小时间水印决定窗口的时间水印,并触发窗口计算。

目前Table-的使用存在一些限制。 首先,两个流的窗口类型必须相同,窗口大小也必须相同。 但相关功能尚未实现。

2.新窗型

以下介绍扩展到两种新的窗口类型。

■2.1

有以下要求。 用户希望能够在一天内绘制pv/uv曲线,即在一天内或一个大窗口内输出多个结果,而不是在窗口结束后统一输出结果。 为了响应这一要求,我们扩展了 .

基于,定制。 这个触发器保证了窗口计算不仅在结束后触发,而且SQL中定义的每一个循环都会触发一次窗口计算。

如上图的SQL案例,总窗口大小为1秒,每0.2秒触发一次,因此窗口内会触发5次窗口计算。 并且根据前一个结果计算下一个输出结果。

对此做了一个名为Lazy的优化。 在实际生产过程中,窗口的相同键值多次触发窗口计算后会输出相同的结果。 对于下游来说,不需要重复接收这种数据。 因此,如果配置了Lazy,并且在同一个窗口内的同一个Key下,下一次输出值与上一次完全相同,下游就不会收到这次更新的数据,从而减少下游的存储压力和并发压力。

■2.2

有如下需求,用户希望触发后,不是丢弃迟到的数据,而是再次触发窗口计算。 如果您使用API​​,则可以使用它来完成需求。 但对于SQL来说,目前还没有办法做到。 因此,扩大现有数据并收集后期数据。 同时,迟到的数据不会每有一个数据到来就重新触发窗口计算并输出到下游。 相反,它将重新定义 SQL 中定义的时间间隔。 窗口大小可以减少向下游发送数据的频率。

同时,侧输出流也会使用累加数据时的逻辑来进行另一次聚合。 这里需要注意的是,如果下游是Hbase这样的数据源,对于同一个key,之前窗口正常触发的数据会被晚到的数据覆盖。 理论上,后期数据与正常窗口触发的数据同等重要,不能互相覆盖。 最后下游会对同一窗口下同一key下接收到的正常数据和延迟数据进行二次聚合。

3、回撤流程优化

接下来介绍一下回撤流上的一些优化。

1. 流表歧义

回顾一下 Flink SQL 中有关回撤流的一些概念。

首先我们来介绍一下连续查询(Query)。 与批处理一次输出一个结果的特性相比,流聚合是指从上游传来一条数据,从下游接收一条更新的数据,即结果由下游不断更新。上游数据。 的。 因此,下游可以收到同一个Key的多个更新结果。

2. 回撤流程

以上图中的SQL为例。 当第二个Java到达聚合运算符时,它将更新第一个Java生成的状态并将结果发送到下游。 如果下游对多次更新的结果不做任何处理,就会产生错误的结果。 针对这种场景,Flink SQL 引入了回撤流的概念。

所谓回撤流程,就是在原始数据前面加一个flag,用True/False来标记。 如果flag为False,则说明这是一条提现消息,通知下游对这条数据进行操作; 如果flag为True,则下游直接执行操作。

■ 2.1 何时产生回撤流

目前,Flink SQL 中生成回撤流有以下四种场景:

解释为什么外连接有回撤。 以Left Outer Join为例,假设左流的数据早于右流的数据到达,则左流的数据会扫描右流的数据的状态。 如果没有找到可以连接的数据,则左流不知道右流。 这条数据是否真的存在于中流中或者右流中对应的数据迟到了。 为了满足Outer join的语义,左流数据仍然会生成一个Join数据并发送给下游,类似于MySQL Left Join,左流的字段填充的是正常的表字段值,而右流填充Null,然后输出到下游,如下图:

(图片来自云栖社区)

稍后,如果右流对应的数据到达,则会扫描左流的状态并再次加入。 这时,为了保证语义的正确性,需要将这段已经输出到下游的特殊数据撤回,同时将最新的Join上的数据输出到下游。 注意,对于同一个key,如果有一次回撤,就不会再有第二次回撤,因为如果这个key的数据晚到的话,就可以join另一个流上对应的数据。

■ 2.2 如何处理提现消息

下面介绍Flink中处理撤销消息的逻辑。

对于中间计算节点来说,是由上图中的四个标志位来控制的。 这些标志表明当前节点是否生成信息或产生信息,以及当前节点是否会消费该信息。 这4个标志位可以决定整个的生成和处理逻辑。

对于Sink节点,目前Flink中有k、nk、k三种sink类型。 如果k收到的上游数据是消息,会直接报错,因为它只能描述-Only语义; nk可以处理信息,如果上游算子发送了消息,它就会对该消息进行操作,如果上游算子发送了什么是正常的更新信息,它就会对该消息进行操作; k可以理解为nk的一些性能优化。 如果Sink数据源支持幂等操作,或者支持基于某个Key的操作,则k在SQL翻译时会将上游Key传递给Table Sink,然后根据该Key进行操作。

■ 2.3 相关优化

我们根据回撤流程做了如下优化。

产生回撤信息的最根本原因之一是不断向下游多次发送更新结果。 因此,为了减少更新频率和并发量,可以将一部分更新结果积累起来再发送出去。 如图所示:

这样就减少了向下游发送撤回消息的频率。

针对Sink节点做了一些优化,在AGG节点和Sink节点之间做了Cache,减轻Sink节点的压力。 当召回消息聚合在Cache中时,当满足Cache的触发条件时,更新的数据将被统一发送到Sink节点。 以下图SQL为例:

参考优化前后的输出结果,可以看出优化后下游接收的数据量减少了。 例如,当用户Sam尝试向下游发送撤回消息时,先创建一层缓存,可以减少下游接收的数据量。 减少很多。

4. 未来规划

下面简单介绍一下我们团队后续的工作计划:

 
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON