Flink小知识: Window详解

官方文档:
https://ci.apache.org/projects/flink/flink-docs-release-1.13/zh/docs/dev/datastream/operators/windows/

1、什么是Window

  • Flink 底层引擎是一个流式引擎,认为 Batch 是 Streaming 的一个特例,在上面实现了流处理和批处理。而窗口(window)就是从 Streaming 到 Batch 的一个桥梁。
  • Window窗口就在一个无界流中设置起始位置和终止位置,让无界流变成有界流,并且在有界流中进行数据处理

2、Window窗口分类

Window窗口在无界流中设置起始位置和终止位置的方式可以有两种:

  • 根据时间设置
  • 根据窗口数据量(count)设置

根据窗口的类型划分:

  • 滚动窗口
  • 滑动窗口

根据数据流类型划分:

  • Keyed Window:基于分组后的数据流之上做窗口操作
  • Global Window:基于未分组的数据流之上做窗口操作

Keyed Window

stream
       .keyBy(...)               <-  keyed versus non-keyed windows
       .window(...)              <-  required: "assigner"//用于定义每一条数据分配到那一个窗口中去
      [.trigger(...)]            <-  optional: "trigger" //用于触发窗口计算或清除元素等操作
      [.evictor(...)]            <-  optional: "evictor" //用于在窗口函数计算之前(后)对满足驱逐条件的数据做过滤
      [.allowedLateness(...)]    <-  optional: "lateness" (else zero)//窗口最终关闭时间
      [.sideOutputLateData(...)] <-  optional: "output tag" //用于保存迟到数据的侧流输出
       .reduce/aggregate/apply()      <-  required: "function"//用于对窗口内数据进行 增量或全量计算
      [.getSideOutput(...)]      <-  optional: "output tag" //获取侧流输出结果

Global Window

stream
       .windowAll(...)           <-  required: "assigner"
      [.trigger(...)]            <-  optional: "trigger" (else default trigger)
      [.evictor(...)]            <-  optional: "evictor" (else no evictor)
      [.allowedLateness(...)]    <-  optional: "lateness" (else zero)
      [.sideOutputLateData(...)] <-  optional: "output tag" (else no side output for late data)
       .reduce/aggregate/apply()      <-  required: "function"
      [.getSideOutput(...)]      <-  optional: "output tag"

2.1 Window Assigners(窗口适配器)

WindowAssigner是负责将每一个到来的元素分配给一个或者多个窗口(window),
tumbling windows, sliding windows, session windows and global windows

  • Tumbling Windows: 滚动窗口,窗口范围固定,窗口之间没有数据重叠,如图所示

在这里插入图片描述

// tumbling event-time windows 基于事件时间做窗口计算
input
    .keyBy(<key selector>)
    .window(TumblingEventTimeWindows.of(Time.seconds(5)))
    .<windowed transformation>(<window function>);

// tumbling processing-time windows 基于系统计算时间做窗口计算
input
    .keyBy(<key selector>)
    .window(TumblingProcessingTimeWindows.of(Time.seconds(5)))
    .<windowed transformation>(<window function>);
  • Sliding Window: 滑动窗口,窗口内的数据有重叠,如下图所示
    在这里插入图片描述
// sliding event-time windows 基于事件时间 每隔5s 统计 10s内的数据
input
    .keyBy(<key selector>)
    .window(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5)))
    .<windowed transformation>(<window function>);

// sliding processing-time windows 基于系统时间 每隔5s 统计 10s内的数据
input
    .keyBy(<key selector>)
    .window(SlidingProcessingTimeWindows.of(Time.seconds(10), Time.seconds(5)))
    .<windowed transformation>(<window function>);
  • Session Windows: 会话窗口
    在规定的时间内如果没有数据活跃接入,则认为窗口结束,然后触发窗口计算结果,如下图所示,
    在这里插入图片描述
// event-time session windows with static gap
input
    .keyBy(<key selector>)
    .window(EventTimeSessionWindows.withGap(Time.minutes(10)))
    .<windowed transformation>(<window function>);
    
// event-time session windows with dynamic gap
//SessionWindowTimeGapExtractor 实现
input
    .keyBy(<key selector>)
    .window(EventTimeSessionWindows.withDynamicGap((element) -> {
        // determine and return session gap
    }))
    .<windowed transformation>(<window function>);
  • Global Windows: 全局窗口 存储所有数据,用户需自定义触发器 trigger
    在这里插入图片描述
  • Count Window: 数量窗口 这里不具体讲解了,它是基于 GlobalWindows+ trigger+ evictor来实现的
		
    public WindowedStream<T, KEY, GlobalWindow> countWindow(long size) {
    		//PurgingTrigger 当size 满足要求是 触发计算且清除窗口内数据
        return window(GlobalWindows.create()).trigger(PurgingTrigger.of(CountTrigger.of(size)));
    }

    /** 当slide 满足要求是 保留窗口内最新的size的数据且触发计算且
     * Windows this {@code KeyedStream} into sliding count windows.
     * @param size The size of the windows in number of elements.
     * @param slide The slide interval in number of elements.
     */ 
    public WindowedStream<T, KEY, GlobalWindow> countWindow(long size, long slide) {
        return window(GlobalWindows.create())
                .evictor(CountEvictor.of(size))
                .trigger(CountTrigger.of(slide));
    }

2.2 Window Functions 窗口函数

窗口函数定义了针对窗口内元素的计算逻辑,窗口函数大概分为两类:

  • 增量聚合函数,聚合原理:窗口内保存一个中间聚合结果,随着新元素的加入,不断对该值进行更新
    这类函数通常非常节省空间 ReduceFunction、AggregateFunction属于增量聚合函数
  • 全量聚合函数,聚合原理:收集窗口内的所有元素,并且在执行的时候对他们进行遍历,这种聚合
    函数通常需要占用更多的空间(收集一段时间的数据并且保存),但是它可以支持更复杂的逻辑
    ProcessWindowFunction、WindowFunction属于全量窗口函数

注意:这两类函数可以组合搭配使用

  • ReduceFunction
    注意: 通过两个输入的参数进行合并输出一个同类型的参数的过程
input
    .keyBy(<key selector>)
    .window(<window assigner>)
    .reduce(new ReduceFunction<Tuple2<String, Long>>() {
      public Tuple2<String, Long> reduce(Tuple2<String, Long> v1, Tuple2<String, Long> v2) {
        return new Tuple2<>(v1.f0, v1.f1 + v2.f1);
      }
    });
  • AggregateFunction
private static class AverageAggregate
    implements AggregateFunction<Tuple2<String, Long>, Tuple2<Long, Long>, Double> {
  @Override //初始化
  public Tuple2<Long, Long> createAccumulator() {
    return new Tuple2<>(0L, 0L);
  }

  @Override //新旧值累加
  public Tuple2<Long, Long> add(Tuple2<String, Long> value, Tuple2<Long, Long> accumulator) {
    return new Tuple2<>(accumulator.f0 + value.f1, accumulator.f1 + 1L);
  }

  @Override //返回结果
  public Double getResult(Tuple2<Long, Long> accumulator) {
    return ((double) accumulator.f0) / accumulator.f1;
  }

  @Override //聚合
  public Tuple2<Long, Long> merge(Tuple2<Long, Long> a, Tuple2<Long, Long> b) {
    return new Tuple2<>(a.f0 + b.f0, a.f1 + b.f1);
  }
}

DataStream<Tuple2<String, Long>> input = ...;

input
    .keyBy(<key selector>)
    .window(<window assigner>)
    .aggregate(new AverageAggregate());
  • ProcessWindowFunction
input
  .keyBy(<key selector>)
  .window(<window assigner>)
  .reduce(new MyReduceFunction(), new MyProcessWindowFunction());

// Function definitions

private static class MyReduceFunction implements ReduceFunction<SensorReading> {

  public SensorReading reduce(SensorReading r1, SensorReading r2) {
      return r1.value() > r2.value() ? r2 : r1;
  }
}

private static class MyProcessWindowFunction
    extends ProcessWindowFunction<SensorReading, Tuple2<Long, SensorReading>, String, TimeWindow> {

  public void process(String key,
                    Context context,
                    Iterable<SensorReading> minReadings,
                    Collector<Tuple2<Long, SensorReading>> out) {
      SensorReading min = minReadings.iterator().next();
      out.collect(new Tuple2<Long, SensorReading>(context.window().getStart(), min));
  }
}

2.3 Triggers 触发器

触发器定义了窗口何时准备好被窗口处理。每个窗口分配器默认都有一个触发器,例如: EventTimeWindows窗口的触发器是 EventTimeTrigger作为默认触发器

注意: GlobalWindow默认的触发器时NeverTrigger,该触发器从不出发,所以在使用GlobalWindow时必须自定义触发器。
在这里插入图片描述

2.4 Evictors (驱逐器)

驱逐器(evitor)可以在触发器触发之前或者之后,或者窗口函数被应用之前清理窗口中的元素。
在这里插入图片描述

2.5 Allowed Lateness (允许延迟)

Flink允许为窗口操作指定一个最大允许时延,允许时延指定了元素可以晚到多长时间,默认情况下是0。例如: 当处理event-time的window时,可能会出现元素晚到的情况(即Flink用来跟踪event-time进度的watermark已经过了元素所属窗口的最后时间,属于当前窗口的数据才到达)。

input
    .keyBy(<key selector>)
    .window(<window assigner>)
    .allowedLateness(<time>)//默认为0,即watermark超过窗口时间就计算且销毁窗口
    .<windowed transformation>(<window function>);

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值