声明: 1. 本文为我的个人复习总结, 并非那种从零基础开始普及知识 内容详细全面, 言辞官方的文章
2. 由于是个人总结, 所以用最精简的话语来写文章
3. 若有错误不当之处, 请指出
一、背压机制:
下游处理数据 处理不过来时, 会使上游降低发送数据的速度
产生的原因:
- 系统资源不足
- 频繁发生GC, 导致经常暂停应用程序
- 数据倾斜
这个发生数据倾斜的分区处理数据 处理不过来 - 外部依赖
Source端 和 Sink端 读写性能太差
二、数据倾斜:
流式数据 不开窗的话是没法预聚合的
keyBy之前:
问题: Kafka有的分区数据量多, 有的分区数据量低
解决: Flink消费数据时使用rebalance算子将数据均匀分配
keyBy之后:
使用两阶段聚合:
非窗口聚合:
第一阶段进行拼接 随机数 进行部分聚合, 第二阶段去掉 拼接的随机数 进行最终聚合
窗口聚合:
第一阶段进行拼接 随机数 & WindowEndTime 进行部分聚合, 第二阶段去掉拼接的随机数, 然后groupBy WindowEndTime进行最终聚合
keyBy之后非窗口聚合操作:
问题:
需求: 计算hello的个数
现有4个hello, 拼接随机数后发往两个分区:
第一阶段: 1号分区为(hello_1,1),(hello_1,2), 2号分区为(hello_2,1),(hello_2,2)
第二阶段: 去掉拼接的随机数后, 得到数据(hello,1),(hello,2), (hello,1),(hello,2)
可见输入的数据并没有减少 & 数据被重复计算了
解决:
使用LocalKeyBy的思想, 自己维护一个状态, 使聚合后只返回一条数据;
那样第二阶段去掉拼接的随机数后 得到的数据就是 (hello,2),(hello,2)
keyBy之后窗口聚合操作:
第一阶段: 1号分区为(hello_1_WindowEndTime,1),(hello_1_WindowEndTime,2),
2号分区为(hello_2_WindowEndTime,1),(hello_2_WindowEndTime,2)
第二阶段: 得到数据(hello_1_WindowEndTime,2),(hello_1_WindowEndTime,2),
去掉拼接的随机数, 然后groupBy WindowEndTime即可得到每个窗口的hello个数聚合值
三、FlinkSQL调优:
分组聚合优化:
- 开启MiniBatch微批处理, 提升吞吐量
- 开启LocalGlobal(需要先开启MiniBatch), 处理热点数据问题(LocalKeyBy的思想)
- 开启Split Distinct
count(distinct 热点数据) 的效果并不好, 而split distinct底层做了优化
split 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
- 使用filter 代替 case when, filter底层做了优化
TopN优化:
- 使用最优算法
两个条件:
- 有主键
- 排序字段的更新是单调的, 且排序方向与单调方向相反
例如count是递增的, 同时使用递减排序 order by count desc
- 增大Cache内存大小, 提高缓存命中率, 减少访问磁盘的次数
高效去重方案:
保留首行
- 如果要是取第一, 就使用rank后取第一行数据
- 如果要是取倒数第一, 就使用rank desc后取第一行数据
这样只保留一条数据即可, 不用保留n条数据