【翻译】Flink Table Api & SQL — 性能调优 — 流式聚合
本文翻译自官网:Streaming Aggregation https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table/tuning/streaming_aggregation_optimization.html
SQL是用于数据分析的最广泛使用的语言。Flink的Table API和SQL使用户能够以更少的时间和精力定义高效的流分析应用程序。而且,Flink Table API和SQL得到了有效的优化,它集成了许多查询优化和优化的运算符实现。但是并非默认情况下会启用所有优化,因此对于某些工作负载,可以通过打开某些选项来提高性能。
在此页面中,我们将介绍一些有用的优化选项以及流聚合的内部原理,这将在某些情况下带来很大的改进。
注意:当前,仅Blink计划程序支持此页面中提到的优化选项。
注意:当前,仅对无边界聚合支持流聚合优化。将来将支持窗口聚合的优化。
默认情况下,无界聚合运算符一个一个地处理输入记录,即(1)从状态读取累加器,(2)将记录累加/缩回到累加器,(3)将累加器写回到状态,(4)下一条记录将从(1)重新进行处理。此处理模式可能会增加StateBackend的开销(尤其是对于RocksDB StateBackend)。此外,生产中非常常见的数据偏斜会使问题恶化,并使工作容易承受背压情况。
小批量聚合
小型批处理聚合的核心思想是将一组输入缓存在聚合运算符内部的缓冲区中。当触发输入以进行处理时,每个键只需一个操作即可访问状态。这样可以大大减少状态开销并获得更好的吞吐量。但是,这可能会增加一些延迟,因为它会缓冲一些记录而不是立即处理它们。这是吞吐量和延迟之间的权衡。
下图说明了小批量聚合如何减少状态操作。
MiniBatch 优化默认情况下处于禁用状态。为了使这种优化,您应该设置 table.exec.mini-batch.enabled
,table.exec.mini-batch.allow-latency
和table.exec.mini-batch.size
。请参阅配置页面以获取更多详细信息。
以下示例显示如何启用这些选项。
// instantiate table environment
val tEnv: TableEnvironment = ... // access flink configuration
val configuration = tEnv.getConfig().getConfiguration()
// set low-level key-value options
configuration.setString("table.exec.mini-batch.enabled", "true") // enable mini-batch optimization
configuration.setString("table.exec.mini-batch.allow-latency", "5 s") // use 5 seconds to buffer input records
configuration.setString("table.exec.mini-batch.size", "5000") // the maximum number of records can be buffered by each aggregate operator task
局部全局聚合
提出将局部聚合分为两个阶段来解决数据倾斜问题,即先在上游进行局部聚合,然后在下游进行全局聚合,这类似于MapReduce中的Combine + Reduce模式。例如,考虑以下SQL:
SELECT color, sum(id)
FROM T
GROUP BY color
数据流中的记录可能会倾斜,因此聚合运算符的某些实例会比其他实例处理更多的记录,这会导致热点。本地聚合可以帮助将具有相同密钥的一定数量的输入累加到单个累加器中。全局汇总将仅接收减少的累加器,而不是大量的原始输入。这可以大大减少网络改组和状态访问的成本。每次本地聚合累积的输入数量基于最小批处理间隔。这意味着本地-全局聚合取决于启用了小批量优化。
下图显示了本地全局聚合如何提高性能。
以下示例显示了如何启用本地全局聚合。
// instantiate table environment
val tEnv: TableEnvironment = ... // access flink configuration
val configuration = tEnv.getConfig().getConfiguration()
// set low-level key-value options
configuration.setString("table.exec.mini-batch.enabled", "true") // local-global aggregation depends on mini-batch is enabled
configuration.setString("table.exec.mini-batch.allow-latency", "5 s")
configuration.setString("table.exec.mini-batch.size", "5000")
configuration.setString("table.optimizer.agg-phase-strategy", "TWO_PHASE") // enable two-phase, i.e. local-global aggregation
分割不同的聚合
局部全局优化可有效消除常规聚合的数据偏斜,例如SUM,COUNT,MAX,MIN,AVG。但是,在处理不同的聚合时,其性能并不令人满意。
例如,如果我们要分析今天有多少唯一用户登录。我们可能有以下查询:
SELECT day, COUNT(DISTINCT user_id)
FROM T
GROUP BY day
如果distinct key(即user_id)的值稀疏,则COUNT DISTINCT不能减少记录。即使启用了局部全局优化,它也无济于事。因为累加器仍包含几乎所有原始记录,并且全局聚合将成为瓶颈(大多数繁重的累加器由一项任务处理,即在同一天)。
此优化的想法是将不同的聚合(例如COUNT(DISTINCT col)
)分为两个级别。第一次聚合由组密钥和其他存储桶密钥混洗。使用来计算存储桶密钥HASH_CODE(distinct_key) % BUCKET_NUM
。BUCKET_NUM
默认为1024,可以通过table.optimizer.distinct-agg.split.bucket-num
选项配置。第二次聚合由原始组密钥改组,并用于SUM
聚合来自不同存储桶的COUNT DISTINCT值。由于相同的唯一键将仅在同一存储桶中计算,因此转换是等效的。存储桶密钥充当附加组密钥的角色,以分担组密钥中的热点负担。存储桶关键字使工作具有可伸缩性,以解决不同聚合中的数据偏斜/热点。
拆分非重复聚合后,上述查询将自动重写为以下查询:
SELECT day, SUM(cnt)
FROM (
SELECT day, COUNT(DISTINCT user_id) as cnt
FROM T
GROUP BY day, MOD(HASH_CODE(user_id), 1024)
)
GROUP BY day
下图显示了拆分的非重复聚合如何提高性能(假设颜色代表天,字母代表user_id)。
注意:上面是最简单的示例,可以从此优化中受益。除此之外,Flink 支持分裂更复杂的聚集查询,例如,一个以上的具有不同的不同密钥(例如不同的集合COUNT(DISTINCT a), SUM(DISTINCT b)
),与其他非重复的聚合工作(例如SUM
,MAX
,MIN
,COUNT
)。
注意:但是,当前,拆分优化不支持包含用户定义的AggregateFunction的聚合。
以下示例显示如何启用拆分非重复聚合优化。
// instantiate table environment
val tEnv: TableEnvironment = ... tEnv.getConfig // access high-level configuration
.getConfiguration // set low-level key-value options
.setString("table.optimizer.distinct-agg.split.enabled", "true") // enable distinct agg split
在不同的聚合上使用FILTER修饰符
在某些情况下,用户可能需要从不同维度计算UV(唯一访客)的数量,例如Android的UV,iPhone的UV,Web的UV和总UV。许多用户将选择CASE WHEN
支持此功能,例如:
SELECT
day,
COUNT(DISTINCT user_id) AS total_uv,
COUNT(DISTINCT CASE WHEN flag IN ('android', 'iphone') THEN user_id ELSE NULL END) AS app_uv,
COUNT(DISTINCT CASE WHEN flag IN ('wap', 'other') THEN user_id ELSE NULL END) AS web_uv
FROM T
GROUP BY day
但是,在这种情况下,建议使用 FILTER 语法而不是CASE WHEN。因为FILTER
它更符合SQL标准,并且将获得更多的性能改进。 FILTER
是用于聚合函数的修饰符,用于限制聚合中使用的值。将上面的示例替换为FILTER
修饰符,如下所示:
SELECT
day,
COUNT(DISTINCT user_id) AS total_uv,
COUNT(DISTINCT user_id) FILTER (WHERE flag IN ('android', 'iphone')) AS app_uv,
COUNT(DISTINCT user_id) FILTER (WHERE flag IN ('wap', 'other')) AS web_uv
FROM T
GROUP BY day
Flink SQL优化器可以识别同一唯一键上的不同过滤器参数。例如,在上面的示例中,所有三个COUNT DISTINCT都在user_id
列上。然后Flink可以只使用一个共享状态实例,而不是三个状态实例,以减少状态访问和状态大小。在某些工作负载中,这可以显着提高性能。
欢迎关注Flink菜鸟公众号,会不定期更新Flink(开发技术)相关的推文
【翻译】Flink Table Api & SQL — 性能调优 — 流式聚合的更多相关文章
- Flink Table Api & SQL 翻译目录
Flink 官网 Table Api & SQL 相关文档的翻译终于完成,这里整理一个安装官网目录顺序一样的目录 [翻译]Flink Table Api & SQL —— Overv ...
- 【翻译】Flink Table Api & SQL — 配置
本文翻译自官网:Configuration https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table/config.h ...
- 【翻译】Flink Table Api & SQL — 用户定义函数
本文翻译自官网:User-defined Functions https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/tabl ...
- 【翻译】Flink Table Api & SQL —— 连接到外部系统
本文翻译自官网:Connect to External Systems https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev ...
- 【翻译】Flink Table Api & SQL — 内置函数
本文翻译自官网:Built-In Functions https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table/fu ...
- 【翻译】Flink Table Api & SQL — 流概念
本文翻译自官网:Streaming Concepts https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table/st ...
- 【翻译】Flink Table Api & SQL — Hive —— 在 scala shell 中使用 Hive 连接器
本文翻译自官网:Use Hive connector in scala shell https://ci.apache.org/projects/flink/flink-docs-release-1 ...
- 【翻译】Flink Table Api & SQL — Hive —— Hive 函数
本文翻译自官网:Hive Functions https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table/hive/h ...
- 【翻译】Flink Table Api & SQL — SQL客户端Beta 版
本文翻译自官网:SQL Client Beta https://ci.apache.org/projects/flink/flink-docs-release-1.9/dev/table/sqlCl ...
随机推荐
- [TJOI2018]最长上升子序列
Link 动态维护LIS? 观察题目:在第 i 轮操作时,将数字 i 插入 插入的数字是当前最大的 如果答案与上次不同,新的LIS必以 i 结尾 以 i 结尾的LIS无法再伸长(因为比 i 小的都插入 ...
- Java Spring Boot 一些调试技巧
配置文件的管理 有的时候我们希望开发环境和测试环境的配置文件放在一起可以快速切换 spring boot 为我们提供了很方便的的选项 在 application.properties 中只需要添加属性 ...
- springBoot 利用Idea打包部署
springBoot 打包部署 1 项目如图: 2 依赖打包插件 3 打包操作 4 运行项目:
- Spring的三大核心思想:IOC(控制反转),DI(依赖注入),AOP(面向切面编程)
Spring核心思想,IoC与DI详解(如果还不明白,放弃java吧) 1.IoC是什么? IoC(Inversion of Control)控制反转,IoC是一种新的Java编程模式,目前很多 ...
- 1.创建SpringMVC项目
1.搭建环境 在Configure下点击Settings 找到Maven修改配置目的是使用自己配置的Maven以及阿里云镜像 开始创建项目 写入自己的公司名,项目名点击下一步 添加信息加快maven配 ...
- 使用docker部署微服务
https://my.oschina.net/silenceyawen/blog/1819472 http://jvm123.com/2019/08/docker-shi-yong.html 从201 ...
- webpack配合babel使用
一.babel介绍 ①Babel 是一个 JavaScript 编译器,可以把ES6的语法转为兼容浏览器的ES5语法 ②Babel中文官网:https://www.babeljs.cn/ ③Babel ...
- Django 数据库与ORM
一.数据库的配置 1 django默认支持sqlite,mysql, oracle,postgresql数据库. <1> sqlite django默认使用sqlite的数据库,默认自带 ...
- js 判断一个数是否在数组中
,,,,,,,); ; ; i < arr.length; i++) { ){ console.log(i); flag=; break; } } ){ console.log("66 ...
- 【POJ1426】Find The Multiple
本题传送门 本题知识点:深度优先搜索 | 宽度优先搜索 题意很简单,让我们找一个只有1和0组成的十位数是n的倍数的数. 这题一开始吓到我了--因为Output里说输出的长度最长不超过100位???那是 ...