大数据技术之 Flink 优化之FlinkSQL 调优

  本博客总结为B站尚硅谷大数据Flink2.0调优,Flink性能优化视频的笔记总结。尚硅谷https://so.csdn.net/so/search?q=%E5%B0%9A%E7%A1%85%E8%B0%B7&spm=1001.2101.3001.7020大数据Flink2.0调优,Flink性能优化https://www.bilibili.com/video/BV1Q5411f76P

FlinkSQL 官网配置参数:

//nightlies.apache.org/flink/flink-docs-release-1.13/docs/dev/table/config/https://ci.apache.org/projects/flink/flink-docs-release-1.13/dev/table/config.html


1、 设置空闲状态保留时间

Flink SQL 新手有可能犯的错误,其中之一就是忘记设置空闲状态保留时间导致状态爆炸。

列举两个场景:

➢ FlinkSQL 的 regular join(inner、left、right),左右表的数据都会一直保存在状态里,不会清理!要么设置 TTL,要么使用 FlinkSQL 的 interval join。

➢ 使用 Top-N 语法进行去重,重复数据的出现一般都位于特定区间内(例如一小时或一天内),过了这段时间之后,对应的状态就不再需要了。

Flink SQL 可以指定空闲状态(即未更新的状态)被保留的最小时间,当状态中某个 key对应的状态未更新的时间达到阈值时,该条状态被自动清理:

#API 指定

tableEnv.getConfig().setIdleStateRetention(Duration.ofHours(1));

#参数指定

Configuration configuration = tableEnv.getConfig().getConfiguration();

configuration.setString("table.exec.state.ttl", "1 h");

2、 开启 MiniBatch

MiniBatch 是微批处理,原理是缓存一定的数据后再触发处理,以减少对 State 的访问,从而提升吞吐并减少数据的输出量。MiniBatch 主要依靠在每个 Task 上注册的 Timer 线程来触发微批,需要消耗一定的线程调度性能。

MiniBatch 默认关闭,开启方式如下:

// 初始化 table environment

TableEnvironment tEnv = ...

// 获取 tableEnv 的配置对象

Configuration configuration = tEnv.getConfig().getConfiguration();

// 设置参数:

// 开启 miniBatch

configuration.setString("table.exec.mini-batch.enabled", "true");

// 批量输出的间隔时间

configuration.setString("table.exec.mini-batch.allow-latency", "5 s");

// 防止 OOM 设置每个批次最多缓存数据的条数,可以设为 2 万条

configuration.setString("table.exec.mini-batch.size", "20000");

➢ 适用场景

微批处理通过增加延迟换取高吞吐,如果有超低延迟的要求,不建议开启微批处理。通常对于聚合的场景,微批处理可以显著的提升系统性能,建议开启。

注意事项:

1)目前,key-value 配置项仅被 Blink planner 支持。

2)1.12 之前的版本有 bug,开启 miniBatch,不会清理过期状态,也就是说如果设置状态的 TTL,无法清理过期状态。1.12 版本才修复这个问题。

参考 ISSUE:[FLINK-17096] Mini-batch group aggregation doesn't expire state even if state ttl is enabled - ASF JIRAhttps://issues.apache.org/jira/browse/FLINK-17096


3、 开启 LocalGlobal

3.1 原理概述

LocalGlobal 优 化 将 原 先 的 Aggregate 分 成 Local+Global 两 阶 段 聚 合 , 即MapReduce 模型中的 Combine+Reduce 处理模式。第一阶段在上游节点本地攒一批数据进行聚合(localAgg),并输出这次微批的增量值(Accumulator)。第二阶段再将收到的 Accumulator 合并(Merge),得到最终的结果(GlobalAgg)。

LocalGlobal 本质上能够靠 LocalAgg 的聚合筛除部分倾斜数据,从而降低 GlobalAgg的热点,提升性能。结合下图理解 LocalGlobal 如何解决数据倾斜的问题。

由上图可知:

⚫ 未开启 LocalGlobal 优化,由于流中的数据倾斜,Key 为红色的聚合算子实例需要处理更多的记录,这就导致了热点问题。

⚫ 开启 LocalGlobal 优化后,先进行本地聚合,再进行全局聚合。可大大减少 GlobalAgg的热点,提高性能。

LocalGlobal 开启方式:

1)LocalGlobal 优化需要先开启 MiniBatch,依赖于 MiniBatch 的参数。

2)table.optimizer.agg-phase-strategy: 聚合策略。默认 AUTO,支持参数 AUTO、

TWO_PHASE(使用 LocalGlobal 两阶段聚合)、ONE_PHASE(仅使用 Global 一阶段聚合)。

// 初始化 table environment

TableEnvironment tEnv = ...

// 获取 tableEnv 的配置对象

Configuration configuration = tEnv.getConfig().getConfiguration();

// 设置参数:

// 开启 miniBatch

configuration.setString("table.exec.mini-batch.enabled", "true");

// 批量输出的间隔时间

configuration.setString("table.exec.mini-batch.allow-latency", "5 s");

// 防止 OOM 设置每个批次最多缓存数据的条数,可以设为 2 万条

configuration.setString("table.exec.mini-batch.size", "20000");

// 开启 LocalGlobal

configuration.setString("table.optimizer.agg-phase-strategy", "TWO_PHASE");

注意事项:

1)需要先开启 MiniBatch

2)开启 LocalGlobal 需要 UDAF 实现 Merge 方法。

3.2 提交案例:统计每天每个 mid 出现次数
bin/flink run \

-t yarn-per-job \

-d \

-p 5 \

-Drest.flamegraph.enabled=true \

-Dyarn.application.queue=test \

-Djobmanager.memory.process.size=1024mb \

-Dtaskmanager.memory.process.size=2048mb \

-Dtaskmanager.numberOfTaskSlots=2 \

-c com.atguigu.flink.tuning.SqlDemo \

/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar \

--demo count

可以看到存在数据倾斜。

3.3 提交案例:开启 miniBatch 和 LocalGlobal
bin/flink run \

-t yarn-per-job \

-d \

-p 5 \

-Drest.flamegraph.enabled=true \

-Dyarn.application.queue=test \

-Djobmanager.memory.process.size=1024mb \

-Dtaskmanager.memory.process.size=2048mb \

-Dtaskmanager.numberOfTaskSlots=2 \

-c com.atguigu.flink.tuning.SqlDemo \

/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar \

--demo count \

--minibatch true \

--local-global true

从 WebUI 可以看到分组聚合变成了 Local 和 Global 两部分,数据相对均匀,且没有数据倾斜。


4、 开启 Split Distinct

LocalGlobal 优化针对普通聚合(例如 SUM、COUNT、MAX、MIN 和 AVG)有较好

的效果,对于 DISTINCT 的聚合(如 COUNT DISTINCT)收效不明显,因为 COUNT

DISTINCT 在 Local 聚合时,对于 DISTINCT KEY 的去重率不高,导致在 Global 节点仍然

存在热点。

4.1 原理概述

之前,为了解决 COUNT DISTINCT 的热点问题,通常需要手动改写为两层聚合(增加按 Distinct Key 取模的打散层)。

从 Flink1.9.0 版 本 开 始 , 提 供 了 COUNT DISTINCT 自 动 打 散 功 能 , 通 过HASH_CODE(distinct_key) % BUCKET_NUM 打散,不需要手动重写。Split Distinct 和LocalGlobal 的原理对比参见下图。

Distinct 举例:

SELECT a, COUNT(DISTINCT b)

FROM T

GROUP BY a

手动打散举例:

SELECT a, SUM(cnt)

FROM (

SELECT a, COUNT(DISTINCT b) as cnt

FROM T

GROUP BY a, MOD(HASH_CODE(b), 1024)

)

GROUP BY a

Split Distinct 开启方式

默认不开启,使用参数显式开启:

⚫ table.optimizer.distinct-agg.split.enabled: true,默认 false。

⚫ table.optimizer.distinct-agg.split.bucket-num: Split Distinct 优化在第一层聚合中,被打散的 bucket 数目。默认 1024。

// 初始化 table environment

TableEnvironment tEnv = ...

// 获取 tableEnv 的配置对象

Configuration configuration = tEnv.getConfig().getConfiguration();

// 设置参数:(要结合 minibatch 一起使用)

// 开启 Split Distinct

configuration.setString("table.optimizer.distinct-agg.split.enabled", "true");

// 第一层打散的 bucket 数目

configuration.setString("table.optimizer.distinct-agg.split.bucket-num", "1024");

注意事项:

(1)目前不能在包含 UDAF 的 Flink SQL 中使用 Split Distinct 优化方法。

(2)拆分出来的两个 GROUP 聚合还可参与 LocalGlobal 优化。

(3)该功能在 Flink1.9.0 版本及以上版本才支持。

4.2 提交案例:count(distinct)存在热点问题
bin/flink run \

-t yarn-per-job \

-d \

-p 5 \

-Drest.flamegraph.enabled=true \

-Dyarn.application.queue=test \

-Djobmanager.memory.process.size=1024mb \

-Dtaskmanager.memory.process.size=2048mb \

-Dtaskmanager.numberOfTaskSlots=2 \

-c com.atguigu.flink.tuning.SqlDemo \

/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar \

--demo distinct

可以看到存在热点问题。

4.3 提交案例:开启 split distinct
bin/flink run \

-t yarn-per-job \

-d \

-p 5 \

-Drest.flamegraph.enabled=true \

-Dyarn.application.queue=test \

-Djobmanager.memory.process.size=1024mb \

-Dtaskmanager.memory.process.size=2048mb \

-Dtaskmanager.numberOfTaskSlots=2 \

-c com.atguigu.flink.tuning.SqlDemo \

/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar \

--demo distinct \

--minibatch true \

--split-distinct true

从 WebUI 可以看到有两次聚合,而且有 partialFinal 字样,第二次聚合时已经均匀。


5、 多维 DISTINCT 使用 Filter

5.1 原理概述

在某些场景下,可能需要从不同维度来统计 count(distinct)的结果(比如统计 uv、app 端的 uv、web 端的 uv),可能会使用如下 CASE WHEN 语法。

SELECT

a,

COUNT(DISTINCT b) AS total_b,

COUNT(DISTINCT CASE WHEN c IN ('A', 'B') THEN b ELSE NULL END) AS AB_b,

COUNT(DISTINCT CASE WHEN c IN ('C', 'D') THEN b ELSE NULL END) AS CD_b

FROM T

GROUP BY a

在这种情况下,建议使用 FILTER 语法, 目前的 Flink SQL 优化器可以识别同一唯一键上的不同 FILTER 参数。如,在上面的示例中,三个 COUNT DISTINCT 都作用在 b 列上。此时,经过优化器识别后,Flink 可以只使用一个共享状态实例,而不是三个状态实例,可减少状态的大小和对状态的访问。

将上边的 CASE WHEN 替换成 FILTER 后,如下所示:

SELECT

a,

COUNT(DISTINCT b) AS total_b,

COUNT(DISTINCT b) FILTER (WHERE c IN ('A', 'B')) AS AB_b,

COUNT(DISTINCT b) FILTER (WHERE c IN ('C', 'D')) AS CD_b

FROM T

GROUP BY a
5.2 提交案例:多维 Distinct
bin/flink run \

-t yarn-per-job \

-d \

-p 5 \

-Drest.flamegraph.enabled=true \

-Dyarn.application.queue=test \

-Djobmanager.memory.process.size=1024mb \

-Dtaskmanager.memory.process.size=2048mb \

-Dtaskmanager.numberOfTaskSlots=2 \

-c com.atguigu.flink.tuning.SqlDemo \

/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar \

--demo dim-difcount

5.3 提交案例:使用 Filter
bin/flink run \

-t yarn-per-job \

-d \

-p 5 \

-Drest.flamegraph.enabled=true \

-Dyarn.application.queue=test \

-Djobmanager.memory.process.size=1024mb \

-Dtaskmanager.memory.process.size=2048mb \

-Dtaskmanager.numberOfTaskSlots=2 \

-c com.atguigu.flink.tuning.SqlDemo \

/opt/module/flink-1.13.1/myjar/flink-tuning-1.0-SNAPSHOT.jar \

--demo dim-difcount-filter

通过 WebUI 对比前 10 次 Checkpoint 的大小,可以看到状态有所减小。


6、 设置参数总结

总结以上的调优参数,代码如下:

// 初始化 table environment

TableEnvironment tEnv = ...

// 获取 tableEnv 的配置对象

Configuration configuration = tEnv.getConfig().getConfiguration();

// 设置参数:

// 开启 miniBatch

configuration.setString("table.exec.mini-batch.enabled", "true");

// 批量输出的间隔时间

configuration.setString("table.exec.mini-batch.allow-latency", "5 s");

// 防止 OOM 设置每个批次最多缓存数据的条数,可以设为 2 万条

configuration.setString("table.exec.mini-batch.size", "20000");

// 开启 LocalGlobal

configuration.setString("table.optimizer.agg-phase-strategy", "TWO_PHASE");

// 开启 Split Distinct

configuration.setString("table.optimizer.distinct-agg.split.enabled", "true");

// 第一层打散的 bucket 数目

configuration.setString("table.optimizer.distinct-agg.split.bucket-num", "1024");

// 指定时区

configuration.setString("table.local-time-zone", "Asia/Shanghai");

### Flink 大数据处理优化技巧与最佳实践 #### 原则与方法概述 对于Flink SQL作业中的大状态导致的反压问题,的核心在于减少状态大小以及提高状态访问效率。通过合理配置参数和整逻辑设计可以有效缓解此类瓶颈[^1]。 #### 参数设置建议 针对不同版本下的具体特性差异,在实施任何性能改进措施前应当充分理解当前使用的Flink版本特点及其局限性;同时也要考虑特定应用场景的需求特征来定制化解决方案。这包括但不限于并行度设定、内存分配策略等方面的选择[^2]。 #### 数据流模式优化 采用广播变量机制可作为一种有效的手段用于降低主数据流转过程中所需维护的状态量级。当存在一对多关系的数据集间需频繁交互时,将较小规模的一方作为广播状态保存下来供另一方查询匹配使用不失为明智之举。此方式特别适用于维表Join操作中,其中一方变动相对较少但又必须保持最新记录的情况[^3]。 ```sql -- 创建临时视图以支持后续JOIN操作 CREATE TEMPORARY VIEW dim_table AS SELECT * FROM kafka_source; -- 定义Temporal Table Function以便获取指定时间点上的历史快照 CREATE FUNCTION hist_dim_table AS 'com.example.HistoricalDimTableFunction'; -- 执行带有时态条件约束的JOIN语句 SELECT o.order_id, d.product_name FROM orders o LEFT JOIN LATERAL TABLE(hist_dim_table(o.event_time)) AS d ON o.product_id = d.id; ``` 上述代码片段展示了如何利用Flink SQL实现基于时间戳的历史维度表连接功能,从而确保每次都能准确捕捉到事件发生瞬间对应的最恰当的产品名称信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

渣渣盟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值