Flink SQL TOPN 优化

大数据技术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
     );

解释说明:

  1. 不输出rownum字段,能减小结果表的输出量(无排名优化)

  2. 注意要有时间字段,否则state过期会导致数据错乱(分区字段优化)

  3. 根据上游sum结果排序。排序字段的更新是单调的,且单调方向与排序方向相反(走最优算法)

  4. 声明Sum的参数都是正数,所以Sum的结果是单调递增的,因此TopN能使用优化算法,只获取前100个数据(走最优算法)

图片

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值