1 状态原理
1.1 状态、状态后端、Checkpoint 三者之间的区别及关系?1.2 把状态后端从 FileSystem 变为 RocksDB 后,Flink 任务状态存储会发生那些变化?1.3 什么样的业务场景你会选择 filesystem,什么样的业务场景你会选 rocksdb 状态后端?1.4 Flink SQL API State TTL 的过期机制是 onCreateAndUpdate 还是 onReadAndWrite 2 时间窗口
2.1 watermark 到底是干啥的?应用场景?2.2 一个 Flink 任务中可以既有事件时间窗口,又有处理时间窗口吗?2.3 window 后面跟 aggregate 和 process 的两个窗口计算的区别是什么? 3 常见优化
3.1 为什么 Flink DataStream API 在函数入参或者出参有泛型时,不能使用 lambda 表达式?3.2 Flink 为什么强调 function 实现时,实例化的变量要实现 serializable 接口?3.3 Flink 的并行度可以通过哪几种方式设置,优先级关系是什么?3.4 你是怎么合理的评估 Flink 任务的并行度?3.5 你是怎么合理评估任务最大并行度?3.6 生产环境中,如何快速判断哪个算子存在反压呢?或者说哪个算子出现了性能问题?3.7 反压有哪些危害?3.8 经常碰到哪些问题会任务反压?3.9 怎么缓解、解决任务反压的情况?3.10 实时数据延迟是怎么监控的?报警策略又是怎么制定的?3.11 通过什么样的监控及保障手段来保障实时指标的质量?3.12 operator-state 和 keyed-state 两者的区别?3.13 你认为以后 Flink SQL 的发展趋势是 unbounded 类 SQL 为主还是窗口类 SQL 为主?原因?
1 状态原理 1.1 状态、状态后端、Checkpoint 三者之间的区别及关系?结论:通俗形象的说,炒菜的锅好比状态后端,菜就是状态,Checkpoint 就是炒菜的动作。
⭐ 状态:本质来说就是数据,在 Flink 中,其实就是 Flink 提供给用户的状态编程接口。
比如 flink 中的 MapState,ValueState,ListState。
⭐ 状态后端:Flink 提供的用于管理状态的组件,状态后端决定了以什么样数据结构,什么样的存储方式去存储和管理我们的状态。
Flink 目前官方提供了 memory、filesystem,rocksdb 三种状态后端来存储我们的状态。
⭐ Checkpoint(状态管理):Flink 提供的用于定时将状态后端中存储的状态同步到远程的存储系统的组件或者能力。
为了防止长时间运行的 Flink 任务挂了导致状态丢失,产生数据质量问题,Flink 提供了状态管理(Checkpoint,Savepoint)的能力,把我们使用的状态给管理起来,定时的保存到远程。然后可以在 Flink 任务 failover 时,从远程把状态数据恢复到 Flink 任务中,保障数据质量。
1.2 把状态后端从 FileSystem 变为 RocksDB 后,Flink 任务状态存储会发生那些变化?结论:是否使用 RocksDB 只会影响 Flink 任务中 keyed-state 存储的方式和地方,Flink 任务中的 operator-state 不会受到影响。
首先我们来看看,Flink 中的状态只会分为两类:
⭐ keyed-state:键值状态,如其名字,此类状态是以 k-v 的形式存储,状态值和 key 绑定。Flink 中的 keyby 之后紧跟的算子的 state 就是键值状态;
⭐ operator-state:算子状态,非 keyed-state 的 state 都是算子状态,非 k-v 结构,状态值和算子绑定,不和 key 绑定。
Flink 中的 kafka source 算子中用于存储 kafka offset 的 state 就是算子状态。
如下图所示是 3 种状态后端和 2 种 State 的对应存储关系:
1、⭐ 横向(行)来看,即 Flink 的状态分类。分为 Operator state-backend、Keyed state-backend;
2、⭐ 纵向(列)来看,即 Flink 的状态后端分类。用户可以配置 memory,filesystem,rocksdb 3 种状态后端,在 Flink 任务中生成 MemoryStateBackend,FsStateBackend,RocksdbStateBackend,其声明了整个任务的状态管理后端类型;
3、⭐ 每个格子中的内容就是用户在配置 xx 状态后端(列)时,给用户使用的状态(行)生成的状态后端实例,生成的这个实例就是在 Flink 中实际用于管理用户使用的状态的组件。
因此对应的结论就是:
⭐ Flink 任务中的 operator-state。无论用户配置哪种状态后端(无论是 memory,filesystem,rocksdb),都是使用 DefaultOperatorStateBackend 来管理的,状态数据都存储在内存中,做 Checkpoint 时同步到远程文件存储中(比如 HDFS)。
⭐ Flink 任务中的 keyed-state。
用户在配置 rocksdb 时,会使用 RocksdbKeyedStateBackend 去管理状态;
用户在配置 memory,filesystem 时,会使用 HeapKeyedStateBackend 去管理状态。
因此就有了这个问题的结论,配置 rocksdb 只会影响 keyed-state 存储的方式和地方,operator-state 不会受到影响。
1.3 什么样的业务场景你会选择 filesystem,什么样的业务场景你会选 rocksdb 状态后端?在回答这个问题前,我们先看看每种状态后端的特性:
⭐ MemoryStateBackend
原理:运行时所需的 State 数据全部保存在 TaskManager JVM 堆上内存中,执行 Checkpoint 的时候,会把 State 的快照数据保存到 JobManager 进程 的内存中。执行 Savepoint 时,可以把 State 存储到文件系统中。
适用场景:
基于内存的 StateBackend 在生产环境下不建议使用,因为 State 大小超过 JobManager 内存就 OOM 了,此种状态后端适合在本地开发调试测试,生产环境基本不用。
State 存储在 JobManager 的内存中。受限于 JobManager 的内存大小。
每个 State 默认 5MB,可通过 MemoryStateBackend 构造函数调整。每个 Stale 不能超过 Akka frame 大小。
⭐ FSStateBackend
原理:运行时所需的 State 数据全部保存在 TaskManager 的内存中,执行 Checkpoint 的时候,会把 State 的快照数据保存到配置的文件系统中。TM 是异步将 State 数据写入外部存储。
适用场景:
适用于处理小状态、短窗口、或者小键值状态的有状态处理任务,不建议在大状态的任务下使用 FSStateBackend。比如 ETL 任务,小时间间隔的 TUMBLE 窗口 ,State 大小不能超过 TM 内存。
⭐ RocksDBStateBackend
原理:使用嵌入式的本地数据库 RocksDB 将流计算数据状态存储在本地磁盘中。在执行 Checkpoint 的时候,会将整个 RocksDB 中保存的 State 数据全量或者增量持久化到配置的文件系统中。
适用场景:
最适合用于处理大状态、长窗口,或大键值状态的有状态处理任务。
RocksDBStateBackend 是目前唯一支持增量检查点的后端。
增量检查点非常适用于超大状态的场景。比如计算 DAU 这种大数据量去重,大状态的任务都建议直接使用 RocksDB 状态后端。
在生产环境中:
⭐ 如果状态很大,使用 Rocksdb;如果状态不大,使用 Filesystem。
⭐ Rocksdb 使用磁盘存储 State,所以会涉及到访问 State 磁盘序列化、反序列化,性能会收到影响,而 Filesystem 直接访问内存,单纯从访问状态的性能来说 Filesystem 远远好于 Rocksdb。生产环境中实测,相同任务使用 Filesystem 性能为 Rocksdb 的 n 倍,因此需要根据具体场景评估选择。
1.4 Flink SQL API State TTL 的过期机制是 onCreateAndUpdate 还是 onReadAndWrite⭐ 结论:Flink SQL API State TTL 的过期机制目前只支持 onCreateAndUpdate,DataStream API 两个都支持
⭐ 剖析:
onCreateAndUpdate:是在创建 State 和更新 State 时【更新 State TTL】
onReadAndWrite:是在访问 State 和写入 State 时【更新 State TTL】
⭐ 实际踩坑场景:Flink SQL Deduplicate 写法,row_number partition by user_id order by proctime asc,此 SQL 最后生成的算子只会在第一条数据来的时候更新 state,后续访问不会更新 state TTL,因此 state 会在用户设置的 state TTL 时间之后过期。
2 时间窗口 2.1 watermark 到底是干啥的?应用场景?watermark 是用于缓解时间时间的乱序问题的。没错,这个观点是正确的。但是个人认为这只是 watermark 第二重要的作用,其更重要的作用在于可以标识一个 Flink 任务的事件 时间进度。
怎么理解 时间进度?
我们可以现象一下,一个事件时间窗口的任务,如果没有一个 东西 去标识其事件时间的进度,那么这个事件时间的窗口也就是不知道什么时候能够触发了,也就是说这个窗口永远不会触发并且输出结果。
所以要有一个 东西 去标识其事件时间的进度,从而让这个事件时间窗口知道,这个事件时间窗口已经结束了,可以触发计算了。在 Flink 中,这个 东西 就是 watermark。
⭐ 标识 Flink 任务的事件时间进度,从而能够推动事件时间窗口的触发、计算。
⭐ 解决事件时间窗口的乱序问题。
2.2 一个 Flink 任务中可以既有事件时间窗口,又有处理时间窗口吗?结论:一个 Flink 任务可以同时有事件时间窗口,又有处理时间窗口。
为什么我们常见的 Flink 任务要么设置为事件时间语义,要么设置为处理时间语义?
确实,在生产环境中,我们的 Flink 任务一般不会同时拥有两种时间语义的窗口。那么怎么解释开头所说的结论呢?
这里从两个角度进行说明:
⭐ 我们其实没有必要把一个 Flink 任务和某种特定的时间语义进行绑定。
对于事件时间窗口来说,我们只要给它 watermark,能让 watermark 一直往前推进,让事件时间窗口能够持续触发计算就行。
对于处理时间来说更简单,只要窗口算子按照本地时间按照固定的时间间隔进行触发就行。无论哪种时间窗口,主要满足时间窗口的触发条件就行。
⭐ Flink 的实现上来说也是支持的。Flink 是使用一个叫做 TimerService 的组件来管理 timer 的,我们可以同时注册事件时间和处理时间的 timer,Flink 会自行判断 timer 是否满足触发条件,如果是,则回调窗口处理函数进行计算。
2.3 window 后面跟 aggregate 和 process 的两个窗口计算的区别是什么?⭐ aggregate:是增量聚合,来一条数据计算完了存储在累加器中,不需要等到窗口触发时计算,性能较好;
⭐ process:全量函数,缓存全部窗口内的数据,满足窗口触发条件再触发计算,同时还提供定时触发,窗口信息等上下文信息;
⭐ 应用场景:aggregate 一个一个处理的聚合结果向后传递一般来说都是有信息损失的,而 process 则可以更加定制化的处理。
3 常见优化 3.1 为什么 Flink DataStream API 在函数入参或者出参有泛型时,不能使用 lambda 表达式?Flink 类型信息系统是通过反射,获取到 Java class 的方法签名,去获取类型信息的。
以 FlatMap 为例,Flink 在通过反射时会检查及获取 FlatMap collector 的出参类型信息。
但是 lambda 表达式写的 FlatMap 逻辑,会导致反射方法获取类型信息时【直接获取不到】collector 的出参类型参数,所以才会报错。
3.2 Flink 为什么强调 function 实现时,实例化的变量要实现 serializable 接口?其实这个问题可以延伸成 3 个问题:
1、为什么 Flink 要用到 Java 序列化机制。和 Flink 类型系统的数据序列化机制的用途有啥区别?
⭐ Flink 写的函数式编程代码或者说闭包,需要 Java 序列化从 JobManager 分发到 TaskManager,而 Flink 类型系统的数据序列化机制是为了分发数据,不是分发代码,可以用非Java的序列化机制,比如 Kyro。
2、非实例化的变量没有实现 Serializable 为啥就不报错,实例化就报错?
⭐ 编译期不做序列化,所以不实现 Serializable 不会报错,但是运行期会执行序列化动作,没实现 Serializable 接口的就报错了
3、为啥加 transient 就不报错?
⭐ Flink DataStream API 的 Function 作为闭包在网络传输,必须采用 Java 序列化,所以要通过 Serializable 接口标记,根据 Java 序列化的规定,内部成员变量要么都可序列化,要么通过 transient 关键字跳过序列化,否则 Java 序列化的时候会报错。静态变量不参与序列化,所以不用加 transient。
3.3 Flink 的并行度可以通过哪几种方式设置,优先级关系是什么?代码中算子单独设置 > 代码中Env全局设置 > 提交参数 > 默认配置信息
上面的 Flink 并行度优先级由大变小。
3.4 你是怎么合理的评估 Flink 任务的并行度?Flink 任务并行度合理行一般根据峰值流量进行压测评估,并且根据集群负载情况留一定量的 buffer 资源。
如果数据源已经存在,则可以直接消费进行测试
如果数据源不存在,需要自行造压测数据进行测试
对于一个 Flink 任务来说,一般可以按照以下方式进行细粒度设置并行度:
⭐ source 并行度配置:以 kafka 为例,source 的并行度一般设置为 kafka 对应的 topic 的分区数
⭐ transform(比如 flatmap、map、filter 等算子)并行度的配置:这些算子一般不会做太重的操作,并行度可以和 source 保持一致,使得算子之间可以做到 forward 传输数据,不经过网络传输
⭐ keyby 之后的处理算子:建议最大并行度为此算子并行度的整数倍,这样可以使每个算子上的 keyGroup 是相同的,从而使得数据相对均匀 shuffle 到下游算子,如下图为 shuffle 策略
⭐ sink 并行度的配置:sink 是数据流向下游的地方,可以根据 sink 的数据量及下游的服务抗压能力进行评估。
如果 sink 是 kafka,可以设为 kafka 对应 topic 的分区数。注意 sink 并行度最好和 kafka partition 成倍数关系,否则可能会出现如到 kafka partition 数据不均匀的情况。但是大多数情况下 sink 算子并行度不需要特别设置,只需要和整个任务的并行度相同就行。
3.5 你是怎么合理评估任务最大并行度?⭐ 前提:并行度必须 <= 最大并行度
⭐ 最大并行度的作用:合理设置最大并行度可以缓解数据倾斜的问题
⭐ 根据具体场景的不同,最大并行度大小设置也有不同的方式:
在 key 非常多的情况下,最大并行度适合设置比较大(几千),不容易出现数据倾斜,以 Flink SQL 场景举例:row_number = 1 partition key user_id 的 Deduplicate 场景(user_id 一般都非常多)
在 key 不是很多的情况下,最大并行度适合设置不是很大,不然会加重数据倾斜,以 Flink SQL 场景举例:group by dim1,dim2 聚合并且维度值不多的 group agg 场景(dim1,dim2 可以枚举),如果依然有数据倾斜的问题,需要自己先打散数据,缓解数据倾斜
⭐ 最大并行度的使用限制:最大并行度一旦设置,是不能随意变更的,否则会导致检查点或保存点失效;最大并行度设置会影响 MapState 状态划分的 KeyGroup 数,并行度修改后再从保存点启动时,KeyGroup 会根据并行度的设定进行重新分布。
⭐ 最大并行度的设置:最大并行度可以自己设置,也可以框架默认生成;默认的算法是取当前算子并行度的 1.5 倍和 2 的 7 次方比较,取两者之间的最大值,然后用上面的结果和 2 的 15 次方比较,取其中的最小值为默认的最大并行度,非常不建议自动生成,建议用户自己设置。
3.6 生产环境中,如何快速判断哪个算子存在反压呢?或者说哪个算子出现了性能问题?将这个问题拆解成多步来分析:
⭐ 如何知道算子是否有反压?
在 Flink web ui 中,定位到一个具体的算子之后,查看 BackPressure 模块,通过颜色和数值来判断任务的繁忙和反压情况。
若颜色为红色,表示当前算子繁忙,有反压的情况;若颜色为绿色,标识当前算子不繁忙,没有反压。
⭐ 举个实际 Flink 任务案例,这个 Flink 任务中有 Source、FlatMap、Sink 算子,如果 Source 算子有反压,那到底是哪个算子有性能问题呢?
上游算子在 web ui 显示有反压时,一般为下游算子存在性能问题。可以继续往下游排查,如果 FlatMap 也显示有反压,大概率是 Sink 算子存在性能问题;如果 FlatMap 没有显示有反压,大概率是 FlatMap 算子存在性能问题。
⭐ 大多数时候,Flink 会自动将算子 chain 在一起,那怎么判断具体是哪一个算子有问题?
第一种方式:Flink 提供了断开算子链的能力。
DataStream API 中:可以使用 disableChaining() 将 chain 在一起的算子链断开。或者配置 pipeline.operator-chaining: false
.process(xxx)
.uid("process")
.disableChaining() // 将算子链进行断开
.addSink(xxx)
.uid("sink");
SQL API 中:配置 pipeline.operator-chaining: falseCREATE TABLE source_table (
order_number BIGINT,
price DECIMAL(32,2)
) WITH (
'connector' = 'datagen',
'rows-per-second' = '10',
'fields.order_number.min' = '10',
'fields.order_number.max' = '11'
);
CREATE TABLE sink_table (
order_number BIGINT,
price DECIMAL(32,2)
) WITH (
'connector' = 'print'
);
insert into sink_table
select * from source_table
where order_number = 10;
来看 SQL 任务在配置 pipeline.operator-chaining: false 前后的差异。
在配置 pipeline.operator-chaining: false 前,可以看到所有算子都 chain 在一起:
在配置 pipeline.operator-chaining: false 后,可以看到所有算子都没有 chain 在一起:
第二种方式:在 Flink 1.13 中,提供了火焰图,可以通过火焰图定位问题。火焰图需要配置 rest.flamegraph.enabled: true 打开
⭐ 任务处理性能出现瓶颈:以消费 Kafka 为例,大概率会出现消费 Kafka Lag。
⭐ Checkpoint 时间长或者失败:因为某些反压会导致 barrier 需要花很长时间才能对齐,任务稳定性差。
⭐ 整个任务完全卡住。比如在 TUMBLE 窗口算子的任务中,反压后可能会导致下游算子的 input pool 和上游算子的 output pool 满了,这时候如果下游窗口的 watermark 一直对不齐,窗口触发不了计算的话,下游算子就永远无法触发窗口计算了。整个任务卡住。
3.8 经常碰到哪些问题会任务反压?总结:算子的 sub-task 需要处理的数据量 > 能够处理的数据量。一般会实际中会有以下两种问题会导致反压。
⭐ 数据倾斜:当前算子的每个 sub-task 只能处理 1w qps 的数据,而由于数据倾斜,这个算子的其中一些 sub-task 平均算下来 1s 需要处理 2w 条数据,但是实际只能处理 1w 条,从而反压。比如有时候 keyby 的 key 设置的不合理。
⭐ 算子性能问题:下游整个整个算子 sub-task 的处理性能差,输入是 1w qps,当前算子的 sub-task 算下来平均只能处理 1k qps,因此就有反压的情况。比如算子需要访问外部接口,访问外部接口耗时长。
3.9 怎么缓解、解决任务反压的情况?⭐ 事前:解决上述介绍到的 数据倾斜、算子性能 问题。
⭐ 事中:在出现反压时:
限制数据源的消费数据速度。比如在事件时间窗口的应用中,可以自己设置在数据源处加一些限流措施,让每个数据源都能够够匀速消费数据,避免出现有的 Source 快,有的 Source 慢,导致窗口 input pool 打满,watermark 对不齐导致任务卡住。
关闭 Checkpoint。关闭 Checkpoint 可以将 barrier 对齐这一步省略掉,促使任务能够快速回溯数据。我们可以在数据回溯完成之后,再将 Checkpoint 打开。
3.10 实时数据延迟是怎么监控的?报警策略又是怎么制定的?Flink 消费 Source 的 Lag 监控,我们可以把这个监控项升级一下,即 Kafka 到 Flink 延迟。原因如下:
以 Flink 消费 Kafka 为例,几乎所有的任务性能问题都最终能反映到 Kafka 消费 Flink 延迟,所以几乎 100% 的任务性能问题都能由 Kafka 到 Flink 延迟 这个监控发现。
具体的实操时,监控项应该怎么设置呢?
回答:Flink 本地时间戳 - Kafka 中自带的时间戳。
这时候有小伙伴提到,这个只能反映出 Flink 消费 Kafka 的延迟,那具体数据上的延迟怎么反映出来呢。
回答:Flink 本地时间戳 - 数据事件时间戳。
3.11 通过什么样的监控及保障手段来保障实时指标的质量?从 事前、事中、事后 x 任务层面、指标层面 进行监控、保障:
⭐ 事前:
任务层面:根据峰值流量进行压力测试,并且留一定 buffer,用于事前保障任务在资源层面没有瓶颈
指标层面:根据业务要求,上线实时指标前进行相同口径的实时、离线指标的验数,在实时指标的误差不超过业务阈值时,才达到上线要求
⭐ 事中:
任务层面:贴源层监控 Kafka 堆积延迟等报警检测手段,用于事中及时发现问题。比如的普罗米修斯监控 Lag 时长
指标层面:根据指标特点实时离线指标结果对比监控。检测到波动过大就报警。比如最简单的方式是可以通过将实时结果导入到离线,然后定时和离线指标对比
⭐ 事后:
任务层面:对于可能发生的故障类型,构建用于故障修复、数据回溯的实时任务备用链路
指标层面:构建指标修复预案,根据不同的故障类型,判断是否可以使用实时任务进行修复。如果实时无法修复,构建离线恢复链路,以便使用离线数据进行覆写修复
3.12 operator-state 和 keyed-state 两者的区别?详细描述一下上面的问题:
operator-state 和 keyed-state 两者的区别?最大并行度又和它们有什么关系?举个生产环境中经常出现的案例,当用户停止任务、更新代码逻辑并且改变任务并发度时,两种 state 都是怎样进行恢复的?
⭐ 总结如下:
1、operator-state:
⭐ 状态适用算子:所有算子都可以使用 operator-state,没有限制。
⭐ 状态的创建方式:如果需要使用 operator-state,需要实现 CheckpointedFunction(建议) 或 ListCheckpointed 接口
⭐ DataStream API 中,operator-state 提供了 ListState、BroadcastState、UnionListState 3 种用户接口
⭐ 状态的存储粒度:以单算子单并行度粒度访问、更新状态
⭐ 并行度变化时:
ListState:均匀划分到算子的每个 sub-task 上,比如 Flink Kafka Source 中就使用了 ListState 存储消费 Kafka 的 offset,其 rescale 如下图
BroadcastState:每个 sub-task 的广播状态都一样UnionListState:将原来所有元素合并,合并后的数据每个算子都有一份全量状态数据
2、keyed-state:
⭐ 状态适用算子:keyed-stream 后的算子使用。注意就是大家会认为 keyby 后面跟的所有算子都使用的是 keyed-state,但这是错误的 ❌,比如有 keyby.process.flatmap,其中 flatmap 中使用状态的话是 operator-state
⭐ 状态的创建方式:从 context 接口获取具体的 keyed-state
⭐ DataStream API 中,keyed-state 提供了 ValueState、MapState、ListState 等用户接口,其中最常用 ValueState、MapState
⭐ 状态的存储粒度:以单 key 粒度访问、更新状态。
举例,当我们使用 keyby.process,在 process 中处理逻辑时,其实每一次 process 的处理 context 都会对应到一个 key,所以在 process 中的处理都是以 key 为粒度的。
这里经常会有个错误问题 ❌,比如想在 open 方法中访问、更新 state,这是不行的,因为 open 方法在执行时,还没有到正式的数据处理环节,上下文中是没有 key 的。
⭐ 并行度变化时:keyed-state 的重新划分是随着 key-group 进行的。其中 key-group 的个数就是最大并发度的个数。其中一个 key-group 处理一段区间 key 的数据,不同 key-group 处理的 key 是完全不同的。当任务并行度变化时,会将 key-group 重新划分到算子不同的 sub-task 上,任务启动后,任务数据在做 keyby 进行数据 shuffle 时,依然能够按照当前数据的 key 发到下游能够处理这个 key 的 key-group 中进行处理,如下图所示。
注意:最大并行度和 key-group 的个数绑定,所以如果想恢复任务 state,最大并行度是不能修改的。大家需要提前预估最大并行度个数。
3.13 你认为以后 Flink SQL 的发展趋势是 unbounded 类 SQL 为主还是窗口类 SQL 为主?原因?unbounded 类 SQL。个人的观点如下:
⭐ 先来看看为什么最开始发明了窗口类的算子:窗口(可以叫做 bounded)和 unbounded 的差异就在于,unbounded 类产出的结果不是一个固定结果,因为有 retract 机制(即 retract 流);
窗口类的算子出现的最原始的目的就是解决 unbounded 类产出不固定结果的问题,是想要创造一个可以产出固定结果的算子(即 append 流,不考虑 allow_lateness),所以窗口算子类算子可以说是解决 unbounded 的存在的一个问题而诞生的,个人理解是流式任务在 SQL 上能力拓展。
⭐ 计算引擎(Flink)的流批一体:目前批中是没有时间窗口之类的概念的,所以如果想做到流批一体在计算引擎用户接口层的统一的话,unbounded SQL 可以做到这一点
⭐ 流式 SQL 的普及度,用户上手难易程度:目前大多数数据开发都还是离线数据开发,离线数据开发切换到实时数据开发使用 unbounded 类 SQL 的切换难度是会小,不用去学习窗口类的接口
但是在目前全链路 changelog 计算不是非常成熟的场景下,是没法完全摒弃窗口类应用的。目前业界做的好的就是阿里,阿里目前几乎不用窗口类应用,他们有一套成熟的 changelog 链路。
为什么阿里不用窗口类应用,因为窗口类应用天生有一个缺点就是会 丢数。



