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 可以只使用一个共享状态实例,而不是三个状态实例,以减少状态访问和状态大小。在某些工作负载下,可以获得显著的性能提升。

相关推荐
earthzhang2021几秒前
【1007】计算(a+b)×c的值
c语言·开发语言·数据结构·算法·青少年编程
杨枝甘露小码8 分钟前
Python学习之基础篇
开发语言·python
武文斌7735 分钟前
项目学习总结:LVGL图形参数动态变化、开发板的GDB调试、sqlite3移植、MQTT协议、心跳包
linux·开发语言·网络·arm开发·数据库·嵌入式硬件·学习
爱吃喵的鲤鱼38 分钟前
仿mudou——Connection模块(连接管理)
linux·运维·服务器·开发语言·网络·c++
爱吃小胖橘1 小时前
Unity网络开发--超文本传输协议Http(1)
开发语言·网络·网络协议·http·c#·游戏引擎
郝学胜-神的一滴1 小时前
使用Linux的read和write系统函数操作文件
linux·服务器·开发语言·数据库·c++·程序人生·软件工程
小火柴1231 小时前
利用R语言绘制直方图
开发语言·r语言
csbysj20202 小时前
React 表单与事件
开发语言
初圣魔门首席弟子2 小时前
c++ bug 函数定义和声明不一致导致出bug
开发语言·c++·bug