Flink中流式的各种聚合

11.1 MiniBatch 聚合

针对无界聚合算子,说简单点就是把一组输入的数据放到缓存里,减少吞吐的开销 默认情况下,对于无界聚合算子来说,mini-batch 优化是被禁用的。开启这项优化,需要设置选项

复制代码
TableConfig configuration = tEnv.getConfig();
configuration.set("table.exec.mini-batch.enabled", "true"); //开启小批量优化
configuration.set("table.exec.mini-batch.allow-latency", "5 s"); //缓存5秒的输入记录 
configuration.set("table.exec.mini-batch.size", "5000"); // 每个聚合运算符任务可以缓冲的最大记录数

11.2 Local-Global 聚合

Local-Global 聚合是为解决数据倾斜问题提出的,通过将一组聚合分为两个阶段,首先在上游进行本地聚合,然后在下游进行全局聚合,类似于 MapReduce 中的 Combine + Reduce 模式。简单来说就是map端聚合之后reduce处理map端聚合的数据。

复制代码
Configuration configuration = tEnv.getConfig().getConfiguration(); 
configuration.setString("table.exec.mini-batch.enabled", "true"); //本地-全局聚合取决于是否启用了mini-batch 
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"); //启用两阶段聚合,即local-global聚合

11.3 拆分 distinct 聚合

**使用场景:**Local-Global 优化可有效消除常规聚合的数据倾斜,例如 SUM、COUNT、MAX、MIN、AVG。但是在处理 distinct 聚合时,其性能并不令人满意。

如果 distinct key (即 user_id)的值分布稀疏,则 COUNT DISTINCT 不适合减少数据。即使启用了 local-global 优化也没有太大帮助。因为累加器仍然包含几乎所有原始记录,并且全局聚合将成为瓶颈(大多数繁重的累加器由一个任务处理,即同一天)。

这个优化的想法是将不同的聚合(例如 COUNT(DISTINCT col))分为两个级别。第一次聚合由 group key 和额外的 bucket key 进行 shuffle。bucket key 是使用 HASH_CODE(distinct_key) % BUCKET_NUM 计算的。BUCKET_NUM 默认为1024,可以通过 table.optimizer.distinct-agg.split.bucket-num 选项进行配置。第二次聚合是由原始 group key 进行 shuffle,并使用 SUM 聚合来自不同 buckets 的 COUNT DISTINCT 值。由于相同的 distinct key 将仅在同一 bucket 中计算,因此转换是等效的。bucket key 充当附加 group key 的角色,以分担 group key 中热点的负担。bucket key 使 job 具有可伸缩性来解决不同聚合中的数据倾斜/热点。

类比离线中处理数据倾斜时。将key打散成很多份之后再聚合。

如何开启:

复制代码
tEnv.getConfig() .set("table.optimizer.distinct-agg.split.enabled", "true"); // enable distinct agg split

11.4 在 distinct 聚合上使用 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 优化器可以识别相同的 distinct key 上的不同过滤器参数。例如,在上面的示例中,三个 COUNT DISTINCT 都在 user_id 一列上。Flink 可以只使用一个共享状态实例,而不是三个状态实例,以减少状态访问和状态大小。在某些工作负载下,可以获得显著的性能提升。

相关推荐
励志不掉头发的内向程序员4 小时前
【LibreCAD 2D架构】从鼠标点击到图形创建:RS_ActionDrawLine交互流程与状态机解析
开发语言·c++·qt·学习·系统架构·计算机外设·交互
www_aiyuanma_vip5 小时前
Java个人博客系统
java·开发语言
leoZ2315 小时前
第 7 篇:进阶——校验、联动、列表页
开发语言·前端·javascript·vue.js·人工智能·目标检测·ecmascript
luj_17685 小时前
虚实交融中的真实人物塑造
c语言·开发语言·网络·经验分享·算法
haolin123.5 小时前
STL vector底层揭秘:从构造到迭代器失效
开发语言·c++·算法
大模型码小白5 小时前
ChatGPT 的回答也能被结构化抓取?AI Bot Scraper 对比测评
服务器·开发语言·数据库·人工智能·spring·chatgpt
尘中远5 小时前
给C++工业软件搭建 Agent
开发语言·c++·qt·ai·agent
1001101_QIA6 小时前
从 CPU 到 GPU:高速缓存、指令并行与 CUDA 执行原理入门
java·后端·spring
卓怡学长6 小时前
w156一周穿搭App的设计与实现
java·spring boot·spring·maven·intellij-idea
三8446 小时前
redis三大机制:配置、持久化、主从复制(getshell 原理地基)
开发语言·php