一文搞懂Windows Function 窗口函数

1、窗口函数的分类

窗口函数,即数据划分窗口后可以调用的处理函数。

l 全量函数:窗口先缓存所有元素,等到触发条件后对窗口内的全量元素执行计算。

l 增量函数:窗口保存一份中间数据,每流入一个新元素,新元素与中间数据两两合一,生成新的中间数据。

2、增量聚合函数

指窗口每进入一条数据就计算一次

实现方法(常见的增量聚合函数如下):
reduce(reduceFunction)
aggregate(aggregateFunction)
sum()
min()
max()

reduce接受两个相同类型的输入,生成一个同类型输出,所以泛型就一个 <T>
maxBy、minBy、sum这3个底层都是由reduce实现的
aggregate的输入值、中间结果值、输出值它们3个类型可以各不相同,泛型有<T, ACC, R>

AggregateFunction 

AggregateFunction 比 ReduceFunction 更加的通用,它有三个参数:输入类型(IN)、累加器类型(ACC)和输出类型(OUT)

输入类型是输入流中的元素类型,AggregateFunction有一个add方法可以将一个输入元素添加到一个累加器中。该接口还具有创建初始累加器(createAccumulator方法)、将两个累加器合并到一个累加器(merge方法)以及从累加器中提取输出(类型为OUT)的方法。

下面是一个案例

给定一组数据按照班级计算平均分

 public static final Tuple3[] ENGLISH = new Tuple3[] {
            Tuple3.of("class1", "张三", 100L),
            Tuple3.of("class1", "李四", 40L),
            Tuple3.of("class1", "王五", 60L),
            Tuple3.of("class2", "赵六", 20L),
            Tuple3.of("class2", "小七", 30L),
            Tuple3.of("class2", "小八", 50L)
    };
package com.bigdata.Windows;

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.AggregateFunction;
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

/**
 * @基本功能:
 * @program:FlinkDemo
 * @author: 乐友陈
 * @create:2024-11-25 15:23:10
 **/
public class AggDemo {


    public static final Tuple3[] ENGLISH = new Tuple3[] {
            Tuple3.of("class1", "张三", 100L),
            Tuple3.of("class1", "李四", 40L),
            Tuple3.of("class1", "王五", 60L),
            Tuple3.of("class2", "赵六", 20L),
            Tuple3.of("class2", "小七", 30L),
            Tuple3.of("class2", "小八", 50L),
    };

    public static void main(String[] args) throws Exception {


        //1. env-准备环境
        // 1. 初始化执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        // 设置运行模式为自动
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);

        // 2. source-加载数据
        // 创建数据流源,使用预定义的英文数据集
        DataStreamSource<Tuple3<String, String, Long>> dataStreamSource = env.fromElements(ENGLISH);

        // 3. transformation-转换数据
        // 按照元组的第一个元素进行分组
        dataStreamSource.keyBy(new KeySelector<Tuple3<String, String, Long>, String>() {
            @Override
            public String getKey(Tuple3<String, String, Long> Tuple3) throws Exception {
                // 返回元组的第一个元素作为键
                return Tuple3.f0;
            }
        })
        // 使用计数窗口,每3个元素进行一次聚合操作
        .countWindow(3)
        // 使用聚合函数对数据进行处理
        .aggregate(new AggregateFunction<Tuple3<String, String, Long>, Tuple3<String,Long,Integer>, Tuple2<String,Double>>() {
            // 初始化累加器
            Tuple3<String,Long,Integer> tuple3 = Tuple3.of(null,0L, 0);

            @Override
            public Tuple3<String,Long, Integer> createAccumulator() {
                // 创建并返回初始累加器
                return tuple3;
            }

            @Override
            public Tuple3<String,Long,Integer> add(Tuple3<String, String, Long> value, Tuple3<String,Long,Integer> rs) {
                // 对每个元素的分数进行累加,并增加计数
                long tmpScore = rs.f1 + value.f2;
                Integer tmpCount = rs.f2 + 1;
                String tmpName = value.f0;
                // 返回更新后的累加器
                return Tuple3.of(tmpName, tmpScore, tmpCount);
            }

            @Override
            public Tuple2<String, Double> getResult(Tuple3<String,Long,Integer> leijiaqi) {
                // 计算平均分并返回结果
                return Tuple2.of(leijiaqi.f0, (double)leijiaqi.f1 / leijiaqi.f2);
            }

            @Override
            public Tuple3<String, Long, Integer> merge(Tuple3<String, Long, Integer> leijiaqiA, Tuple3<String, Long, Integer> leijiaqiB) {
                // 合并两个累加器
                return Tuple3.of(leijiaqiA.f0,(long)leijiaqiA.f1 + leijiaqiB.f1, leijiaqiA.f2 + leijiaqiB.f2);
            }
        })
        // 4. sink-输出数据
        // 打印聚合后的结果到控制台
        .print();
        //3. transformation-数据处理转换
        //4. sink-数据输出


        //5. execute-执行
        env.execute();
    }
}

3、全量聚合函数

指在窗口触发的时候才会对窗口内的所有数据进行一次计算(等窗口的数据到齐,才开始进行聚合计算,可实现对窗口内的数据进行排序等需求)

实现方法
apply(windowFunction)
process(processWindowFunction)

全量聚合: 窗口需要维护全部原始数据,窗口触发进行全量聚合。


ProcessWindowFunction一次性迭代整个窗口里的所有元素,比较重要的一个对象是Context,可以获取到事件和状态信息,这样我们就可以实现更加灵活的控制,该算子会浪费很多性能,主要原因是不增量计算,要缓存整个窗口然后再去处理,所以要设计好内存。

package com.bigdata.Windows;

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.windowing.WindowFunction;
import org.apache.flink.streaming.api.windowing.assigners.SlidingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.GlobalWindow;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;
import org.apache.flink.util.Collector;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;

import java.util.Properties;

/**
 * @基本功能:
 * @program:FlinkDemo
 * @author: 乐友陈
 * @create:2024-11-25 11:15:11
 **/
public class Demo3 {

    public static void main(String[] args) throws Exception {

        //1. env-准备环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);

        //2. source-加载数据
        Tuple3[] ENGLISH = new Tuple3[] {
                Tuple3.of("class1", "张三", 100L),
                Tuple3.of("class1", "李四", 40L),
                Tuple3.of("class1", "王五", 60L),
                Tuple3.of("class2", "赵六", 20L),
                Tuple3.of("class2", "小七", 30L),
                Tuple3.of("class2", "小八", 50L)
        };
        // 先求每个班级的总分数,再求每个班级的总人数
        DataStreamSource<Tuple3<String,String,Long>> streamSource = env.fromElements(ENGLISH);
        KeyedStream<Tuple3<String, String, Long>, String> keyedStream = streamSource.keyBy(v -> v.f0);
        
        keyedStream.countWindow(3).apply(new WindowFunction<Tuple3<String, String, Long>, Double, String, GlobalWindow>() {
            @Override
            public void apply(String s, GlobalWindow window, Iterable<Tuple3<String, String, Long>> input, Collector<Double> out) throws Exception {
                // 计算总成绩,计算总人数
                int sumScore = 0,sumPerson=0;
                for (Tuple3<String, String, Long> tuple3 : input) {
                    sumScore += tuple3.f2;
                    sumPerson += 1;
                }
                out.collect((double)sumScore/sumPerson);
            }
        }).print();


        //5. execute-执行
        env.execute();
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值