分区(Partitioning)与分组(Grouping):
分区:
- 将数据流划分为多个子流,每个子流由一个或多个子任务处理。
- 通过 keyBy 操作实现,根据键对数据进行分区。
- 数据具体去往哪个分区,是通过指定的 key 值先进行一次 hash 再进行一次MurmurHash,通过上述计算得到的值再与并行度进行相应的计算得到。
- 分区的目的是实现数据并行处理,提高处理性能和吞吐量。
分组:
- 根据特定键将数据进行逻辑上的划分,以便对相同键的数据进行聚合操作。
- 结合窗口操作(如 timeWindow)使用,在分区后的数据流中对相同键的数据进行聚合计算。
- 在聚合操作中,将相同键的事件聚合在一起进行处理。
示例:
java
DataStream<MyEvent> groupedStream = stream
// keyBy 分区操作
.keyBy(event -> event.getKey())
// 分组操作
.timeWindow(Time.minutes(5))
.sum("value");
数据如果具有相同的 key 将一定去往同一个分组和分区,但是同一分区中的数据不一定属于同一组。
分区中的按键分区(Keyed)和非按键分区(Non-Keyed)
- 按键分区窗口(Keyed Windows)
- 经过按键分区keyBy操作后,数据流会按照key被分为多条逻辑流(logical streams),这就是KeyedStream。
- 基于KeyedStream进行窗口操作时,窗口计算会在多个并行子任务上同时执行。相同key的数据会被发送到同一个并行子任务。
- 非按键分区(Non-Keyed Windows)
- 如果没有进行keyBy,那么原始的DataStream就不会分成多条逻辑流。窗口逻辑只能在一个任务(task)上执行,并行度为1。
- 非按键分区的窗口操作,手动调大窗口算子的并行度也是无效的。
bash
# Keyd windows
stream.keyBy().window()
# Non-Keyd windows
stream.windowAll()
窗口的分类
按照截取数据的方式(驱动类型)分类:
- 时间窗口(Time Window): 时间点来截取数据。
- 计数窗口(Count Window): 元素的个数来截取数据。
按照窗口分配数据的规则分类:
- 滚动窗口(Tumbling Window)
- 窗口之间不重叠,也不会有间隔。
- 每个数据都会被分配到唯一的窗口中。
- 可以用时间或者数量定义窗口的大小。
- 滑动窗口(Sliding Window)
- 类似滑动窗户,窗口大小固定,窗口之间可以重叠。
- 参数有窗口的大小和滑动的步长。
- 数据可以同时被分配到多个窗口中,适合结果更新非常快的场景。
- 可以用时间或者数量定义窗口的大小。
- 会话窗口(Session Window)
- 通过会话来对数据进行分组,只能基于时间----会话的超时时间。
- 会话的长度不固定,分区之间窗口没有关联,会话窗口之间一定是不会重叠的,并且可能会留有GapSize.
- 适用保持会话的场景。
- 以及全局窗口(Global Window)
- 没有结束的窗口,包含所有的key,默认不会触发计算。
- 预留用来自定义窗口函数。
窗口计算的4个重要的组成部分:
- assigner(分配器):如何将元素分配给窗口。
- function(计算函数):为窗口定义的计算。其实是一个计算函数,完成窗口内容的计算。
- triger(触发器):在什么条件下触发窗口的计算。
- evictor(退出器):定义从窗口中移除数据。
窗口的其他特性:
- 窗口的区间是左闭右开,窗口的最大时间戳 = end - 1ms。
- 当属于某个窗口的第一个元素到达,Flink 就会创建一个窗口。
- 时间进展 >= 窗口最大时间戳 + 窗口允许延迟时间。这样的窗口就能销毁掉。