Flink Window介绍

一、Window的分类

什么是window?

Flink是一个流处理计算框架,它底层基于流处理引擎,实现了批处理与流处理,在流处理中,为了处理无限的数据集,使用window将无限的数据流切分多个有限的数据块进行计算。

我们按照具体的业务需求将window划分为KeyedWindow以及Non-KeyedWindow,如果是KeyedWindow那么它会有多个并行度来计算窗口中的数据,对于KeyedWindow的并行度则为1。
在这里插入图片描述
以time window为例,按照key分组与不分组的window划分如下图所示:
在这里插入图片描述
Keyed WindowNon-Keyed Window中又分为Count-based window根据元素个数对数据流进行分组切分,Time-based window根据时间对数据流进行分组切分。

Count-based window又有以下分类:

  • Tumbling CountWindow
  • Sliding CountWindow

Time-based window有以下分类:

  • Tumbling Window
  • Sliding Window
  • Session Window

对于Keyed Window还有Global Window,即不划分窗口,所以使用该窗口必须定义触发器。
对于每一个窗口的大小是[start,end),即前闭后开区间。

Tumbling Window

将数据依据固定的窗口长度对数据进行切片,窗口的长度固定,event没有 重叠。
在这里插入图片描述
该窗口的设置可以设置窗口的大小,通过设置时间,时,分,秒等,也可以通过offset来指定偏移量。比如窗口设置为一天,单并不需要从整点开始,即提前8小时开始创建窗口。

DataStream<T> input = ...;

// 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>);

// daily tumbling event-time windows offset by -8 hours.
input
    .keyBy(<key selector>)
    .window(TumblingEventTimeWindows.of(Time.days(1), Time.hours(-8)))
    .<windowed transformation>(<window function>);

Sliding Window

滑动窗口由固定的窗口长度和滑动间隔组成,event有重叠,窗口大小固定。
在这里插入图片描述
该窗口可以设置窗口的大小,以及滑动的距离,也可以设置offset设置窗口的偏移量。

DataStream<T> input = ...;

// sliding event-time windows
input
    .keyBy(<key selector>)
    .window(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5)))
    .<windowed transformation>(<window function>);

// sliding processing-time windows
input
    .keyBy(<key selector>)
    .window(SlidingProcessingTimeWindows.of(Time.seconds(10), Time.seconds(5)))
    .<windowed transformation>(<window function>);

// sliding processing-time windows offset by -8 hours
input
    .keyBy(<key selector>)
    .window(SlidingProcessingTimeWindows.of(Time.hours(12), Time.hours(1), Time.hours(-8)))
    .<windowed transformation>(<window function>);

Session Window

类似于web应用中的session,即一段时间没有接受到新数据就会生成新的窗口,特点是时间无对齐,event不重叠,没有固定的开始时间以及结束时间,只需指定session gap即可,常用于线上用户行为分析。
在这里插入图片描述
在session window设置gap时,可以设置固定gap和动态gap,如要实现动态gap,需要实现SessionWindowTimeGapExtractor接口。使用方式如下:

DataStream<T> input = ...;

// 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
input
    .keyBy(<key selector>)
    .window(EventTimeSessionWindows.withDynamicGap((element) -> {
        // determine and return session gap
    }))
    .<windowed transformation>(<window function>);

// processing-time session windows with static gap
input
    .keyBy(<key selector>)
    .window(ProcessingTimeSessionWindows.withGap(Time.minutes(10)))
    .<windowed transformation>(<window function>);
    
// processing-time session windows with dynamic gap
input
    .keyBy(<key selector>)
    .window(ProcessingTimeSessionWindows.withDynamicGap((element) -> {
        // determine and return session gap
    }))
    .<windowed transformation>(<window function>);

Global Window

有相同key的所有元素分配给相同的单个全局窗口,使用时必须要自定义触发器,注意:使用在Keyed Stream中。
在这里插入图片描述
总结
在这里插入图片描述
除了上述的方法使用window外,还可以使用下面的方法:

Keyed Windows

  • Tumbling time window:.timeWindow(Time.seconds(30))
  • Sliding time window:.timeWindow(Time.seconds(30), Time.seconds(10))
  • Tumbling count window:.countWindow(1000)
  • Sliding count window:.countWindow(1000, 10)
  • Session window:.window(SessionWindows.withGap(Time.minutes(10)))

Non-Keyed Windows

  • Tumbling time window:.timeWindowAll(Time.seconds(30))
  • Sliding time window:.timeWindowAll(Time.seconds(30), Time.seconds(10))
  • Tumbling count window:.countWindowAll(1000)
  • Sliding count window:.countWindowAll(1000, 10)
  • Session window:.window(SessionWindows.withGap(Time.minutes(10)))
二、触发器Trigger&驱逐器Evictor

触发器

触发器决定了一个窗口何时可以被窗口函数处理(条件满足时触发并发出信号)。每一个window都有一个默认的触发器,当默认的触发器不满足你的需求时,可以通过调用trigger(...)来自定义一个触发器来使用。

如果要自定义触发器,只需要实现抽象类Trigger即可,它主要有以下几个抽象方法:

@PublicEvolving
public abstract class Trigger<T, W extends Window> implements Serializable {

	private static final long serialVersionUID = -4104633972991191369L;
	//每个元素被添加到窗口时调用
	public abstract TriggerResult onElement(T element, long timestamp, W window, TriggerContext ctx) throws Exception;
	//当一个已注册的处理时间计时器启动时调用
	public abstract TriggerResult onProcessingTime(long time, W window, TriggerContext ctx) throws Exception;
	//当一个已注册的事件时间计时器启动时调用
	public abstract TriggerResult onEventTime(long time, W window, TriggerContext ctx) throws Exception;
	//是否支持合并
	public boolean canMerge() {
		return false;
	}
	//与状态性触发器相关,当使用session window时,两个触发器对应的窗口合并时,合并两个触发器的状态。
	public void onMerge(W window, OnMergeContext ctx) throws Exception {
		throw new UnsupportedOperationException("This trigger does not support merging.");
	}
	//相应窗口被清除时触发
	public abstract void clear(W window, TriggerContext ctx) throws Exception;

可以看到上述方法通过返回一个TriggerResult来决定如何对其调用的事件进行处理,主要有以下处理方式:

public enum TriggerResult {

	//什么也不做
	CONTINUE(false, false),
	
	//触发计算并随后清除窗口中的元素
	FIRE_AND_PURGE(true, true),
	
	//触发计算
	FIRE(true, false),
	
	//清除窗口中的数据
	PURGE(false, true);
	......
	}

对于每一个window默认的有一个触发器,比如所有基于EventTime的Window都有一个EventTimeTrigger作为触发器,GlobalWindow的默认触发器是NeverTrigger,即永不触发,所以使用时必须要自定义触发器。当我们使用trigger()来指定触发器时,将覆盖默认的触发器。在Flink中有一些内置的Trigger触发器:

  • EventTimeTrigger:根据由watermark衡量的Event Time进度来触发
  • ProcessingTimeTrigger:根据处理时间来触发
  • CountTrigger :一旦窗口中的元素个数超出了给定的限制就会触发
  • PurgingTrigger :接受另一个触发器作为参数,并将其转换为一个purging触发器(当嵌套触发器触发时,将返回FIRE_AND_PURGE类型的TriggerResult)

驱逐器

驱逐器是可选操作,默认是没有的,它能够在窗口触发器前后删除一些无用的元素,类似于filter的功能。如果自定义Evictor,需要实现Evictor这个接口,它主要有 以下两个方法:

public interface Evictor<T, W extends Window> extends Serializable {
	//在窗口函数之前被应用
	void evictBefore(Iterable<TimestampedValue<T>> elements, int size, W window, EvictorContext evictorContext);
	//在窗口函数之后被应用
	void evictAfter(Iterable<TimestampedValue<T>> elements, int size, W window, EvictorContext evictorContext);
	......
}

Flink不能保证窗口中元素的顺序。这意味着,尽管驱逐者从窗口的开头删除元素,但这些元素不一定是最先或最后到达的元素。在Flink中,还有一些内置的驱逐器:

  • CountEvitor:在窗口中保持用户指定数量的元素,并在窗口的开始处丢弃多余的元素。
  • DeltaEvitor:通过一个DeltaFunction和一个阈值,计算窗口缓存中最后一个元素和剩余的所有元素的delta值,并清除delta值大于或者等于阈值的元素。
  • TimeEvitor:使用一个interval(毫秒数)作为参数,对于一个给定的窗口,它会找出元素中的最大时间戳max_ts,并清除时间戳小于max_tx - interval的元素。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值