Flink:时间和窗口

Fink时间语义

给定一个时间窗口,比如一个小时,统计时间窗口内的数据指标。如何界定哪些数据进入窗口?怎么定义这个时间?Flink提供了3种时间语义。

  • Event Time:指数据流种每个元素或事件自带的事件属性,一般指事件发生事件。由于事件从发生到进入Flink算子之间有很多环节,使用Event Time意味着事件到达有可能是乱序的,没办法确定要等多长时间所有的事件是才能都到达,需要结合Watermark使用;
  • Processing Time: 指使用算子当前节点的操作系统时间
  • Ingestin Time: 是时间到达Flink Source的时间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QNrNNWHl-1689168123798)(C:\Users\务本\AppData\Roaming\Typora\typora-user-images\image-20230611233609998.png)]

设置时间语义

env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime); 

Watermark

Watermark简单说就是一个时间戳,是尽量确保事件输入的完整性;为解决事件乱序问题,实现数据延迟和准确的平衡;

  • 实现机制:数据进入flink,根据输入事件获取对应当前最大的时间戳,周期性的根据事件的最大时间戳生成waterMark发送出去,生成的waterMark过程可以设置延迟事件,waterMark再与时间窗口的数据对比,确认是否触发窗口计算;
/**
* {@code WatermarkGenerator} 可以基于事件或者周期性的生成 watermark。
*/
@Public
public interface WatermarkGenerator<T> {

/**
* 每来一条事件数据调用一次,可以检查或者记录事件的时间戳,或者也可以基于事件数据本身去生成 *watermark。
*/
void onEvent(T event, long eventTimestamp, WatermarkOutput output);

/**
* 周期性的调用,也许会生成新的 watermark,也许不会。
*
* <p>调用此方法生成 watermark 的间隔时间由 {@link ExecutionConfig#getAutoWatermarkInterval()} 决定。
*/
void onPeriodicEmit(WatermarkOutput output);
}
  • Watermark 策略
    • WatermarkStrategy.forMonotonousTimestamps():处理有序事件,按顺序递增,不加延迟
    • WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofSeconds(10)):无序事件,设置延迟时间
#设置Watermark策略
DataStream<MyType> stream = ... 
 
DataStream<MyType> withTimestampsAndWatermarks = stream 
        .assignTimestampsAndWatermarks( 
            WatermarkStrategy 
                .forMonotonousTimestamps() 
                //提取事件内的时间戳
                .withTimestampAssigner(...) 
        ); 

时间窗口类型
  • 滚动窗口:窗口数据有固定的大小,窗口中的数据不会叠加;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xPTlSa08-1689168123800)(C:\Users\务本\AppData\Roaming\Typora\typora-user-images\image-20230612161227747.png)]

DataStream<T> input = ... 

// tumbling event-time windows 
input 
.keyBy(<KeySelector>) 
.window(TumblingEventTimeWindows.of(Time.seconds(5))) 
.<window function>(...) 

// tumbling processing-time windows 
input 
.keyBy(<KeySelector>) 
.window(TumblingProcessingTimeWindows.of(Time.seconds(5))) 
.<window function>(...) 

// 1 hour tumbling event-time windows offset by 15 minutes. 
input 
.keyBy(<KeySelector>) 
.window(TumblingEventTimeWindows.of(Time.hours(1), Time.minutes(15))) 
.<window function>(...)
  • 滑动窗口;窗口数据有固定的大小,并且有生成间隔;
    在这里插入图片描述

  • 会话窗口:窗口数据没有固定的大小,根据用户传入的参数进行划分,窗口数据无叠加。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VyoaWOxw-1689168123801)(C:\Users\务本\AppData\Roaming\Typora\typora-user-images\image-20230612161135012.png)]

时间窗口函数
  • reduce()
  • aggregate()
public interface AggregateFunction<IN, ACC, OUT>  
extends Function, Serializable { 

// 在一次新的Aggregate Function发起时,创建一个新的Accumulator,Accumulator中的值是我们所说的中间状态数据,即ACC数据 
// 以下函数一般在初始化时调用 
ACC createAccumulator(); 

// 当一个新元素流入时,将新元素与ACC数据合并,返回ACC数据 
ACC add(IN value, ACC accumulator); 

// 将两个ACC数据合并 
ACC merge(ACC a, ACC b); 

// 将中间数据转换成结果数据 
OUT getResult(ACC accumulator); 

} 
  • process()
/** 
* 函数接收4个泛型 
* IN:输入类型 
* OUT:输出类型 
* KEY:keyBy()中按照Key分组,Key类型 
* W:窗口类型 
*/ 

public abstract class ProcessWindowFunction<IN, OUT, KEY, W extends Window> extends  
AbstractRichFunction { 

/** 
* 对一个窗口内的元素进行处理,窗口内的元素缓存在Iterable<IN>,进行处理后输出到 
Collector<OUT>中 
* 我们可以输出一到多个结果 
*/ 
public abstract void process(KEY key, Context context, Iterable<IN> elements,  
Collector<OUT> out) throws Exception; 

/**  
* 当窗口执行完毕被清理时,删除各类状态数据 
*/ 
public void clear(Context context) throws Exception {} 

/** 
* 一个窗口的Context,包含窗口的一些元数据、状态数据等。 
*/ 
public abstract class Context implements java.io.Serializable { 

// 返回当前正在处理的窗口 
public abstract W window(); 

// 返回当前Process Time 
public abstract long currentProcessingTime(); 

// 返回当前Event Time对应的Watermark 
public abstract long currentWatermark(); 

// 返回某个Key下的某个窗口的状态 
public abstract KeyedStateStore windowState(); 
// 返回某个Key下的全局状态 
public abstract KeyedStateStore globalState(); 

// 将迟到数据发送到其他位置 
public abstract <X> void output(OutputTag<X> outputTag, X value); 
} 
} 
双流连接(join)

类似mysql指定字段进行jion连接,只不过Flink在一个时间窗口上进行join;目前支持窗口连接和时间间隔连接

  • 窗口连接(windown join)
input1.join(input2) 
.where(<KeySelector>)      <- input1 使用哪个字段作为Key 
.equalTo(<KeySelector>)    <- input2 使用哪个字段作为Key 
.window(<WindowAssigner>)  <- 指定WindowAssigner 
[.trigger(<Trigger>)]      <- 指定Trigger(可选) 
[.evictor(<Evictor>)]      <- 指定Evictor(可选) 
.apply(<JoinFunction>)     <- 指定JoinFunction 

Join的大致过程,两个输入数据流先按key进行分组,再将元素划分到窗口,一个窗口内包含两个数据流的元素,以内连接来关联,形成一个数据对,最后调用JoinFunction对窗口的数据对进行处理;

  • 时间间隔连接(Interval join)
public static class MyProcessFunction extends ProcessJoinFunction<Tuple3<String, 
Long, Integer>, Tuple3<String, Long, Integer>, String> { 
    @Override 
    public void processElement(Tuple3<String, Long, Integer> input1, 
                               Tuple3<String, Long, Integer> input2, 
                               Context context, 
                               Collector<String> out) { 
          out.collect("input 1: " + input1.toString() + ", input 2: " +  
input2.toString()); 
    } 
} 
 
// 使用Event Time 
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime); 
// 数据流有3个字段:(Key, 时间戳, 数值) 
DataStream<Tuple3<String, Long, Integer>> input1 = ... 
DataStream<Tuple3<String, Long, Integer>> input2 = ... 
 
DataStream<String> intervalJoinResult = input1.keyBy(i -> i.f0) 
      .intervalJoin(input2.keyBy(i -> i.f0)) 
      .between(Time.milliseconds(-5), Time.milliseconds(10)) 
      .process(new MyProcessFunction());

迟到数据
  • 直接丢弃,默认处理方法
  • 则路输出
  • 更新计算结果
final OutputTag<T> lateOutputTag = new OutputTag<T>("late-data"){}; 
 
DataStream<T> input = ... 
 
SingleOutputStreamOperator<T> result = input 
    .keyBy(<key selector>) 
    .window(<window assigner>)
    //设定允许迟到时间,更新数据
    .allowedLateness(<time>) 
    //则路输出
    .sideOutputLateData(lateOutputTag) 
    .<windowed transformation>(<window function>); 
 
DataStream<T> lateStream = result.getSideOutput(lateOutputTag); 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值