• Flink sql 流式去重


    在大数据的处理过程中会出现很多汇总类指标的计算,比如计算当日的每个类目下的用户的订单信息,就需要按类目分组,对用户做去重。Flink sql 提供了 “去重” 功能,可以在流模式的任务中做去重操作。

    官网文档 去重 

    官网链接: [去重](https://ci.apache.org/projects/flink/flink-docs-release-1.12/zh/dev/table/sql/queries.html#%E5%8E%BB%E9%87%8D)

    ** 注意 仅 Blink planner 支持去重。

    去重是指对在列的集合内重复的行进行删除,只保留第一行或最后一行数据。 在某些情况下,上游的 ETL 作业不能实现精确一次的端到端,这将可能导致在故障恢复 时,sink 中有重复的记录。 由于重复的记录将影响下游分析作业的正确性(例如,SUM、COUNT), 所以在进一步分析之前需要进行数据去重。

    与 Top-N 查询相似,Flink 使用 ROW_NUMBER() 去除重复的记录。理论上来说,去重是一个特殊的 Top-N 查询,其中 N 是 1 ,记录则是以处理时间或事件事件进行排序的。

    以下代码展示了去重语句的语法:

    SELECT [column_list]
    FROM (
       SELECT [column_list],
         ROW_NUMBER() OVER ([PARTITION BY col1[, col2...]]
           ORDER BY time_attr [asc|desc]) AS rownum
       FROM table_name)
    WHERE rownum = 1

    参数说明:

    ROW_NUMBER(): 从第一行开始,依次为每一行分配一个唯一且连续的号码。
    PARTITION BY col1[, col2...]: 指定分区的列,例如去重的键。
    ORDER BY time_attr [asc|desc]: 指定排序的列。所指定的列必须为 时间属性, 目前 Flink 支持 处理时间属性 和 事件时间属性 。升序( ASC )排列指只保留第一行,而降序排列( DESC )则指保留最后一行。
    WHERE rownum = 1: Flink 需要 rownum = 1 以确定该查询是否为去重查询。
    以下的例子描述了如何指定 SQL 查询以在一个流计算表中进行去重操作。

    val env = StreamExecutionEnvironment.getExecutionEnvironment
    val tableEnv = TableEnvironment.getTableEnvironment(env)
    
    // 从外部数据源读取 DataStream
    val ds: DataStream[(String, String, String, Int)] = env.addSource(...)
    // 注册名为 “Orders” 的 DataStream
    tableEnv.createTemporaryView("Orders", ds, $"order_id", $"user", $"product", $"number", $"proctime".proctime)
    
    // 由于不应该出现两个订单有同一个order_id,所以根据 order_id 去除重复的行,并保留第一行
    val result1 = tableEnv.sqlQuery(
        """
          |SELECT order_id, user, product, number
          |FROM (
          |   SELECT *,
          |       ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY proctime DESC) as row_num
          |   FROM Orders)
          |WHERE row_num = 1
        """.stripMargin)

    ** 注以上内容来自官网

    demo

    默认模式和 mini-batch 模式下都支持去重,但是默认模型是基于全局的,mini-batch 是基于 mini-batch 配置的
    默认模式 下,去重是基于全局的,要么只输出第一条数据( asc 升序,第一条数据 rownum=1),要么全部输出(desc 降序,依次来的每条数据 rownum 都是 1)
    mini-batch 模式下,数据是基于批次生效

    去重 sql 如下:

    --- 去重查询
    -- kafka source
    CREATE TABLE user_log (
      user_id VARCHAR
      ,item_id VARCHAR
      ,category_id VARCHAR
      ,behavior INT
      ,ts TIMESTAMP(3)
      ,process_time as proctime()
      , WATERMARK FOR ts AS ts
    ) WITH (
      'connector' = 'kafka'
      ,'topic' = 'user_behavior'
      ,'properties.bootstrap.servers' = 'localhost:9092'
      ,'properties.group.id' = 'user_log'
      ,'scan.startup.mode' = 'group-offsets'
      ,'format' = 'json'
    );
    
    ---sink table
    CREATE TABLE user_log_sink (
      user_id VARCHAR
      ,item_id VARCHAR
      ,category_id VARCHAR
      ,behavior INT
      ,ts TIMESTAMP(3)
      ,num BIGINT
      ,primary key (user_id) not enforced
    ) WITH (
    'connector' = 'upsert-kafka'
      ,'topic' = 'user_behavior_sink'
      ,'properties.bootstrap.servers' = 'localhost:9092'
      ,'properties.group.id' = 'user_log'
      ,'key.format' = 'json'
      ,'key.json.ignore-parse-errors' = 'true'
      ,'value.format' = 'json'
      ,'value.json.fail-on-missing-field' = 'false'
      ,'value.fields-include' = 'ALL'
    );
    
    -- insert
    insert into user_log_sink(user_id, item_id, category_id,behavior,ts,num)
    SELECT user_id, item_id, category_id,behavior,ts,rownum
    FROM (
       SELECT user_id, item_id, category_id,behavior,ts,
         ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY process_time desc) AS rownum -- desc use the latest one,
       FROM user_log)
    WHERE rownum=1-- 只能使用 rownum=1,如果写 rownum=2(或<10),每个分区只会输出一条数据(小于是多条)rownum=2的,看起来基于全局去重了

    mini-batch 配置参数

    configuration.setString("table.exec.mini-batch.enabled", "true");
    configuration.setString("table.exec.mini-batch.allow-latency", "5 s");
    configuration.setString("table.exec.mini-batch.size", "5000");

    去重是基于参数 "table.exec.mini-batch.allow-latency" 和 "table.exec.mini-batch.size" 生效,满足一个就算一个“批次”,每个批次每个 category_id 只输出一次 (可以控制数据源的数据,只输出一个用户一个 category_id,测试这两个参数)

     rownum=1 的问题

    看了下源码,去重部分结构是这样的

    大致分成两部分,分别以类: DeduplicateFunctionBase 和 MiniBatchDeduplicateFunctionBase 为基类,分别实现了 ProcTimeDeduplicateKeepFirstRowFunction 、ProcTimeDeduplicateKeepLastRowFunction 、RowTimeDeduplicateFunction 和 ProcTimeMiniBatchDeduplicateKeepFirstRowFunction、ProcTimeMiniBatchDeduplicateKeepLastRowFunction、RowTimeMiniBatchDeduplicateFunction 两个分支的数据
    简单的区别就是: DeduplicateFunctionBase 接收的数据是单条的,mini-batch 的接收的数据都是集合

    RowTimeDeduplicateFunction

    @Override
    public void processElement(RowData input, Context ctx, Collector<RowData> out) throws Exception {
        deduplicateOnRowTime(
                state,
                input,
                out,
                generateUpdateBefore,
                generateInsert,
                rowtimeIndex,
                keepLastRow);
    }

    RowTimeMiniBatchDeduplicateFunction

    @Override
    public void finishBundle(Map<RowData, List<RowData>> buffer, Collector<RowData> out) throws Exception {
        for (Map.Entry<RowData, List<RowData>> entry : buffer.entrySet()) {
            RowData currentKey = entry.getKey();
            List<RowData> bufferedRows = entry.getValue();
            ctx.setCurrentKey(currentKey);
            miniBatchDeduplicateOnRowTime(
                    state,
                    bufferedRows,
                    out,
                    generateUpdateBefore,
                    generateInsert,
                    rowtimeIndex,
                    keepLastRow);
        }
    }

    所以就不需要问为什么 rownum 只能等于 1 了。

    新的问题又来了: 为什么没看 mini-batch 的时候没有输出没有去重呢?

    答: PARTITION BY category_id ORDER BY process_time desc 在 desc 的情况下,每次进来的数据 process_time 都是大于之前的数据,新数据的 rownum=1,所以 每条数据都输出了

    完整代码参见:https://github.com/springMoon/sqlSubmit 

    欢迎关注Flink菜鸟公众号,会不定期更新Flink(开发技术)相关的推文

  • 相关阅读:
    认识ASP.NET 中的 AppDomain
    试验总结1 改变递归函数中的执行内容
    试验总结2 break与continue
    开篇的话
    01复杂度3 二分查找
    02线性结构2 一元多项式的乘法与加法运算
    01复杂度2 Maximum Subsequence Sum
    02线性结构4 Pop Sequence
    01复杂度1 最大子列和问题
    02线性结构1 两个有序链表序列的合并
  • 原文地址:https://www.cnblogs.com/Springmoon-venn/p/14506681.html
Copyright © 2020-2023  润新知