Flink TableAPI和SQL(二十一)UDF(四)表聚合函数(Table Aggregate Functions)

文章目录

用户自定义表聚合函数(UDTAGG)可以把一行或多行数据(也就是一个表)聚合成另一张表,结果表中可以有多行多列。很明显,这就像表函数和聚合函数的结合体,是一个“多对多”的转换。

自定义表聚合函数需要继承抽象类 TableAggregateFunction。TableAggregateFunction 的结构和原理与 AggregateFunction 非常类似,同样有两个泛型参数<T, ACC>,用一个 ACC 类型的累加器(accumulator)来存储聚合的中间结果。聚合函数中必须实现的三个方法,在TableAggregateFunction 中也必须对应实现:

1) createAccumulator()
创建累加器的方法,与 AggregateFunction 中用法相同。

2) accumulate()
聚合计算的核心方法,与 AggregateFunction 中用法相同。

3) emitValue()
所有输入行处理完成后,输出最终计算结果的方法。

这个方法对应着 AggregateFunction中的 getValue()方法;区别在于 emitValue 没有输出类型,而输入参数有两个:第一个是 ACC类型的累加器,第二个则是用于输出数据的“收集器”out,它的类型为 Collect。所以很明显,表聚合函数输出数据不是直接 return,而是调用 out.collect()方法,调用多次就可以输出多行数据了;这一点与表函数非常相似。

另外,emitValue()在抽象类中也没有定义,无法 override,必须手动实现。表聚合函数得到的是一张表;在流处理中做持续查询,应该每次都会把这个表重新计算输出。如果输入一条数据后,只是对结果表里一行或几行进行了更新(Update),这时我们重新计算整个表、全部输出显然就不够高效了。为了提高处理效率,TableAggregateFunction 还提供了一个 emitUpdateWithRetract()方法,它可以在结果表发生变化时,以“撤回”(retract)老数据、发送新数据的方式增量地进行更新。如果同时定义了 emitValue()和 emitUpdateWithRetract()
两个方法,在进行更新操作时会优先调用 emitUpdateWithRetract()。

表聚合函数相对比较复杂,它的一个典型应用场景就是 Top N 查询。比如我们希望选出一组数据排序后的前两名,这就是最简单的 TOP-2 查询。没有线程的系统函数,那么我们就可以自定义一个表聚合函数来实现这个功能。在累加器中应该能够保存当前最大的两个值,每当来一条新数据就在 accumulate()方法中进行比较更新,最终在 emitValue()中调用两次out.collect()将前两名数据输出。

目前 SQL 中没有直接使用表聚合函数的方式,所以需要使用 Table API 的方式来调用:

public class UdfTest_TableAggregateFunction {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

        //1.在创建表的DDL中直接定义时间属性
        String creatDDL = "CREATE TABLE clickTable (" +
                "user_name STRING," +
                "url STRING," +
                "ts BIGINT," +
                "et AS TO_TIMESTAMP( FROM_UNIXTIME(ts / 1000))," + //事件时间  FROM_UNIXTIME() 能转换为年月日时分秒这样的格式 转换秒
                " WATERMARK FOR et AS et - INTERVAL '1' SECOND " + //watermark 延迟一秒
                ")WITH(" +
                " 'connector' = 'filesystem'," +
                " 'path' = 'input/clicks.txt'," +
                " 'format' = 'csv'" +
                ")";

        tableEnv.executeSql(creatDDL);

        //2.注册自定义的表聚合函数
        tableEnv.createTemporarySystemFunction("Top2", Top2.class);

        //3.调用UDF进行查询转换
        String windowAggQuery = "SELECT user_name,COUNT(url) AS cnt,window_start,window_end " +
                "FROM TABLE(" +
                " TUMBLE(TABLE clickTable,DESCRIPTOR(et),INTERVAL '10' SECOND)" +
                ")" +
                "GROUP BY user_name,window_start,window_end";

        Table aggTable = tableEnv.sqlQuery(windowAggQuery);

        //FlinkSQL 对表聚合函数支持并不是很好,这里使用TableAPI方式
        Table resultTable = aggTable.groupBy($("window_end"))
                .flatAggregate(call("Top2", $("cnt")).as("value", "rank"))
                .select($("window_end"), $("value"), $("rank"));

        //4.转换成流打印输出
        tableEnv.toChangelogStream(resultTable).print();

        env.execute();
    }

    //单独定义一个累加器类型,包含了当前最大和第二大的数据
    public static class Top2Accumulator {
        public Long max;
        public Long secondMax;
    }


    //实现自定义的表聚合函数
    public static class Top2 extends TableAggregateFunction<Tuple2<Long, Integer>, Top2Accumulator> {

        @Override
        public Top2Accumulator createAccumulator() {
            //创建累加器(初始化累加器)
            Top2Accumulator top2Accumulator = new Top2Accumulator();
            top2Accumulator.max = Long.MIN_VALUE;
            top2Accumulator.secondMax = Long.MIN_VALUE;
            return top2Accumulator;
        }

        //定义一个更新累加器的方法
        public void accumulate(Top2Accumulator accumulate, Long value) {
            if (value > accumulate.max) {
                accumulate.secondMax = accumulate.max;
                accumulate.max = value;
            } else if (value > accumulate.secondMax) {
                accumulate.secondMax = value;
            }
        }

        //输出结果,获取当前的 Top2
        public void emitValue(Top2Accumulator accumulator, Collector<Tuple2<Long, Integer>> out) {
            if (accumulator.max != Long.MIN_VALUE) {
                out.collect(Tuple2.of(accumulator.max, 1));
            }
            if (accumulator.secondMax != Long.MIN_VALUE) {
                out.collect(Tuple2.of(accumulator.secondMax, 2));
            }
        }
    }
}

这里使用了 flatAggregate()方法,它就是专门用来调用表聚合函数的接口。对 clickTable中数据按 window_end字段进行分组聚合,统计 value 值最大的两个;并将聚合结果的两个字段重命名为 value 和 rank,之后就可以使用 select()将它们提取出来了。

Gitee上的源代码

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Flink 1.14的Table APISQL教程可以在Flink官方文档中找到,其中包括了Table APISQL的基础概念、语法、操作符、函数等内容,还有详细的示例代码和实战案例,非常适合初学者学习和入门。另外,Flink社区也有很多优秀的博客和视频教程,可以帮助大家更深入地理解和应用Table APISQL。 ### 回答2: Flink是一个分布式计算引擎,是Apache Hadoop生态圈中用于处理流式数据的一种解决方案。Flink支持APISQL语言,使得用户可以更加简单地实现流处理任务。而在Flink 1.14中,TableAPISQL引擎则得到了进一步的增强。 TableAPISQL将无需掌握Java或Scala编程语言就可以操作格数据。TableAPI API支持Java和Scala,SQL则支持标准的SQL语言。如果你熟悉SQL语言,那么你很容易上手使用TableAPISQL引擎。 Flink TableAPISQL支持各种类型的格操作,包括选择、过滤、分组、排序、连接等。此外,它们还支持窗口和聚合操作。这使得用户在处理流式数据时可以更加简单易懂地进行复杂的操作。 在Flink 1.14中,TableAPISQL引擎还提供了一系列新功能,包括: 1. 时间特征支持——TableAPISQL中的数据时间戳可以通过时间特征进行定义和控制。通过时间特征,用户可以定义数据的时间属性,例如事件时间或处理时间。 2. 详细的窗口管理——当窗口中的数据到期时,Flink 1.14会自动清除过期数据,避免数据量过大导致性能下降。 3. 支持更多的流数据源——在Flink 1.14中,TableAPISQL引擎可以直接从Kafka、Kinesis、Hive等数据源中读取数据。这可以让用户更加方便地读取数据,而无需编写额外的代码。 TableAPISQL引擎对于Flink用户来说是非常重要的工具,无需掌握Java或Scala编程语言即可操作格数据。并且在Flink 1.14中,这两个工具得到了进一步的增强,包括更好的窗口管理和更多的数据源支持。因此,学习TableAPISQL引擎对于想要使用Flink进行流处理的人来说是非常有益的。 ### 回答3: Flink 1.14 TableAPISQL是一个非常好用的数据处理工具,可帮助数据分析师快速进行数据查询、聚合和处理。下面详细介绍一下Flink 1.14的TableAPISQL教程。 1. 如何配置Flink 1.14的TableAPISQL环境? 在进行Flink 1.14的TableAPISQL开发之前,需要先进行环境的配置。可以在官网下载Flink的安装包,解压后找到/bin目录下的start-cluster.sh脚本进行启动。启动之后,即可通过WebUI的页面查看Flink的运行状态。 2. TableAPI的基本操作 TableAPIFlink的一个高层次数据处理API,可以通过编写代码来进行数据的处理。TableAPI的基本操作有以下几个: (1) 创建Table,可以使用StreamTableEnvironment的fromDataStream或fromTableSource方法,将DataStream或TableSource转换成Table。 (2) Table的转换,可以使用多种转换操作,包括filter、select、orderBy、groupBy、join等。 (3) 将Table转化为DataStream,可以使用StreamTableEnvironment的toDataStream方法。 3. SQL的基本操作 SQLFlink提供的一种快速数据处理方式,用户只需要编写SQL语句即可完成数据处理。SQL的基本操作有以下几个: (1) 注册Table,可以使用StreamTableEnvironment的registerTable或registerTableSource方法,将TableTableSource注册到环境中。 (2) 执行SQL,可以使用StreamTableEnvironment的executeSql方法,执行SQL语句并返回结果。 (3) 将结果转换为DataStream,可以使用StreamTableEnvironment的toDataStream方法。 4. 如何优化Flink 1.14的TableAPISQL的执行效率? 在进行TableAPISQL开发时,为了保证其执行效率,需要注意以下几点: (1) 避免使用复杂的JOIN操作,可以使用Broadcast和TableFunction等方式来避免JOIN操作。 (2) 注意Table的Schema定义,Schema的设计合理与否直接影响SQL性能。 (3) 避免使用无限制的聚合操作,可以进行分批次聚合来避免。 总的来说,Flink 1.14的TableAPISQL是非常强大的数据处理工具,能够帮助开发者快速高效的进行数据处理。上述内容是入门级别的教程,如果想要更深入的了解,可以参考官方文档进行学习。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值