大数据技术AI
Flink/Spark/Hadoop/数仓,数据分析、面试,源码解读等干货学习资料
94篇原创内容
公众号
使用最优算法
_convert/bcd6de69d398473400a879e3e0839213.png)
当TopN的输入是非更新流(例如Source),TopN只有一种算法AppendRank。
当TopN的输入是更新流时(例如经过了AGG/JOIN计算),TopN有2种算法,性能从高到低分别是:UpdateFastRank 和RetractRank。算法名字会显示在拓扑图的节点名字上。
注意:apache社区版的Flink1.12目前还没有UnaryUpdateRank,阿里云实时计算版Flink才有
-
UpdateFastRank :最优算法 需要具备2个条件:
-
输入流有PK(Primary Key)信息,例如ORDER BY AVG。
-
排序字段的更新是单调的,且单调方向与排序方向相反。例如,ORDER BY COUNT/COUNT_DISTINCT/SUM(正数)DESC。如果要获取到优化Plan,则您需要在使用ORDER BY SUM DESC时,添加SUM为正数的过滤条件。
-
AppendFast:结果只追加,不更新
-
RetractRank:普通算法,性能差 不建议在生产环境使用该算法。请检查输入流是否存在PK信息,如果存在,则可进行UpdateFastRank优化。
无排名优化(解决数据膨胀问题)
- TopN语法
SELECT [column_list]
FROM (
SELECT [column_list],
ROW_NUMBER() OVER ([PARTITION BY col1[, col2...]]
ORDER BY col1 [asc|desc][, col2 [asc|desc]...]) AS rownum
FROM table_name)
WHERE rownum <= N [AND conditions]
- 数据膨胀问题
问题描述:根据TopN的语法,rownum字段将作为唯一键的一个字段写入结果表,这可能导致大量记录写入结果表。例如,当排名9的记录(比如product-1001)被更新并且其排名升级为1时,排名1~9的所有记录都会作为更新消息输出到结果表中。如果结果表接收的数据过多,就会成为SQL作业的瓶颈
优化方式:在Top-N查询的外层SELECT子句中省略rownum字段。这是合理的,因为前 N 条记录的数量通常并不多,因此消费者可以自己快速地对记录进行排序。没有rownum字段,在上面的例子中,只需要将改变的记录(product-1001)发送到下游,可以减少很多对结果表的IO
- 使用方式
CREATE TABLE ShopSales ( product_id STRING, category STRING, product_name STRING, sales BIGINT) WITH (...);-- omit row_num field from the outputSELECT product_id, category, product_name, salesFROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) AS row_num FROM ShopSales)WHERE row_num <= 5
为了将上述查询输出到外部存储并得到正确的结果,外部存储必须与 Top-N 查询具有相同的唯一键。在上面的示例查询中,如果 product_id 是查询的唯一键,那么外部表也应该有 product_id 作为唯一键
在无rownum的场景中,对于结果表主键的定义需要特别小心。如果定义有误,会直接导致TopN结果的不正确。无rownum场景中,主键应为TopN上游GROUP BY节点的KEY列表。
增加TopN的Cache大小
TopN为了提升性能有一个State Cache层,Cache层能提升对State的访问效率。TopN的Cache命中率的计算公式为。
cache_hit = cache_sizeparallelism/top_n/partition_key_num
例如,Top100配置缓存10000条,并发50,当PatitionBy的key维度较大时,例如10万级别时,Cache命中率只有1000050/100/100000=5%
,命中率会很低,导致大量的请求都会击中State(磁盘),性能会大幅下降。因此当PartitionKey维度特别大时,可以适当加大TopN的CacheS ize,相对应的也建议适当加大TopN节点的Heap Memory。
- 使用方式
// 初始化
table environment TableEnvironment tEnv = ...
// 获取 tableEnv的配置对象
val configuration = tEnv.getConfig().getConfiguration();
// 默认10000条,调整TopN cahce到20万,那么理论命中率能达200000*50/100/100000 = 100%
configuration.setString("table.exec.topn.cache-size", "200000");
注意:目前源码中标记为实验项,官网中未列出该参数
PartitionBy的字段中要有时间类字段
例如:每天的排名,要带上Day字段。否则TopN的结果到最后会由于State ttl有错乱。
优化后的SQL示例
SELECT student_id,
subject_id,
stat_date,
score
--不输出rownum字段,能减小结果表的输出量(无排名优化)
FROM (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY student_id,
stat_date --注意要有时间字段,否则state过期会导致数据错乱(分区字段优化)
ORDER
BY score DESC --根据上游sum结果排序。排序字段的更新是单调的,且单调方向与排序方向相反(走最优算法)
) AS rownum
FROM (
SELECT student_id,
subject_id,
stat_date,
--重点。声明Sum的参数都是正数,所以Sum的结果是单调递增的,因此TopN能使用优化算法,只获取前100个数据(走最优算法)
sum(score) filter ( WHERE score >= 0) AS score
FROM score_test
WHERE score >= 0
GROUP BY student_id,
subject_id,
stat_date
) a
WHERE rownum <= 100
);
解释说明:
-
不输出rownum字段,能减小结果表的输出量(无排名优化)
-
注意要有时间字段,否则state过期会导致数据错乱(分区字段优化)
-
根据上游sum结果排序。排序字段的更新是单调的,且单调方向与排序方向相反(走最优算法)
-
声明Sum的参数都是正数,所以Sum的结果是单调递增的,因此TopN能使用优化算法,只获取前100个数据(走最优算法)