流式计算中的 Window 计算 课后习题 | 青训营笔记

82 阅读6分钟

这是我参与「第四届青训营 」笔记创作活动的第4天

题目来源:【大数据专场 学习资料二】第四届字节跳动青训营 - 掘金

  1. 复习实时计算产生的背景,与离线计算最主要的区别,以及流式窗口计算的最大挑战

    1. 数据时效性高,离线计算一般是天级别任务,即使是小时级任务,由于上层层层以来,以及任务启动耗时,延迟也比较高,而实时计算能够做到分钟级别
    2. 挑战:乱序数据的影响,需要在时效性和精准之间抉择;此外,需要长期持有计算资源,而批处理是定时调度
  1. watermark的产生、传递、使用原理,以及在各种断流或者上游出现问题的情况下应该如何处理

    1. Watermark定义:当前系统认为的事件时间所在的真实时间。
    2. Watermark产生:一般是从数据的事件时间来产生,产生策略可以灵活多样,最常见的包括使用当前事件时间的时间减去一个固定的delay,来表示可以可以容忍多长时间的乱序。
    3. Watermark传递:这个类似于Checkpoint的制作过程,传递就类似于Checkpoint的barrier,上下游task之间有数据传输关系的,上游就会将watermark传递给下游;下游收到多个上游传递过来的watermark后,默认会取其中最小值来作为自身的watermark,同时它也会将自己watermark传递给它的下游。经过整个传递过程,最终系统中每一个计算单元就都会实时的知道自身当前的watermark是多少。
    4. 使用原理:如果窗口时间小于watermark时间,则认为窗口内所有数据已经到达,可以开始计算
    5. 数据断流:数据断流是很常见的问题,有时候是业务数据本身就有这种特点,比如白天有数据,晚上没有数据。在这种情况下,watermark默认是不会更新的,因为它要取上游subtask发来的watermark中的最小值。此时我们可以用一种IDLE状态来标记这种subtask,被标记为这种状态的subtask,我们在计算watermark的时候,可以把它先排除在外。这样就可以保证有部分partition断流的时候,watermark仍然可以继续更新。
    6. 上游出现问题,某个partition产生过慢:按照per-partition的方式来产生watermark
  1. 三种基本的window的功能和原理

    1. TUMBLE Window 滚动窗口

      1. 原理:根据数据的时间(可以是处理时间,也可以是事件时间)划分到它所属的窗口中windowStart = timestamp - timestamp % windowSize,这条数据所属的window就是[windowStart, windowStart + windowSize)

      2. 功能:按照key将各条数据划分到所属窗口,窗口之间不重叠,窗口的触发,是时间大于等于window end的时候,触发对应的window的输出(计算有可能提前就增量计算好了),目前的实现是给每个window都注册一个timer,通过处理时间或者事件时间的timer来触发window的输出。

    2. HOP Window 滑动窗口

      1. 在HOP窗口中,每条数据是可能会属于多个窗口的(具体属于多少,取决于窗口定义的大小和滑动),比如下图中假设滑动是1h的话,那窗口大小就是2h,这种情况每条数据会属于两个窗口。除了这一点之外,其它的基本跟HOP窗口是类似的,比如也是key级别划分窗口,也是靠timer进行窗口触发输出。
    3. SESSION Window 会话窗口

      1. 会话窗口跟上面两种窗口区别比较大,上面两个窗口的划分,都是根据当前数据的时间就可以直接确定它所属的窗口。会话窗口的话,是一个动态merge的过程。一般会设置一个会话的最大的gap,比如10min。
      2. 那某个key下面来第一条数据的时候,它的window就是 [event_time, event_time + gap),当这个key后面来了另一条数据的时候,它会立即产生一个窗口,如果这个窗口跟之前的窗口有overlap的话,则会将两个窗口进行一个merge,变成一个更大的窗口,此时需要将之前定义的timer取消,再注册一个新的timer。
      3. 所以会话窗口要求所有的聚合函数都必须有实现merge。

  1. window的基本功能扩展有哪些

    1. 迟到数据处理

      1. watermark驱动某个窗口触发输出之后,这个窗口如果后面又来了数据,那这种情况就属于是迟到的数据了
      2. 使用side output方式,把迟到的数据转变成一个单独的流,再由用户自己来决定如何处理这部分数据
      3. 直接drop掉
    2. 增量计算 VS 全量计算

      1. 增量计算:每条数据到来后,直接参与计算(但是还不需要输出结果)
      2. 全量计算:每条数据到来后,先放到一个buffer中,这个buffer会存储到状态里,直到窗口触发输出的时候,才把所有数据拿出来统一进行计算
    3. EMIT触发

      1. 可以提前把窗口内容输出出来的一种机制。比如我们可以配置一个1天的窗口,每隔5s输出一次它的最新结果,那这样下游就可以更快的获取到窗口计算的结果了。
    4. Window Offset

      1.     那么window offset的功能就是可以在计算窗口的时候,可以让窗口有一个偏移。所以最终计算window的公式就变成了:windowStart = timestamp - (timestamp - offset + windowSize) % windowSize
  1. 四种高级的window的优化分别是为了解决什么问题,又是什么原理

    1. Mini-batch

      1. 利用watermark划分mini-batch,再进行计算,这批数据每个key的state访问只有一次,这样在单个key的数据比较集中的情况下,对于状态访问可以有效的降低频率,最终提升性能。
    2. Local-global

      1. 将原本的聚合划分成两阶段,第一阶段先做一个local的聚合,这个阶段不需要数据shuffle,是直接跟在上游算子之后进行处理的;第二个阶段是要对第一个阶段的结果做一个merge
      2. 可以降低数据shuffle的量,同时也可以缓解数据的倾斜。
    3. Distinct状态复用

      1. 把相同字段的distinct计算用一个map的key来存储,在map的value中,用一个bit vector来实现就可以把各个状态复用到一起了。比如一个bigint有64位,可以表示同一个字段的64个filter,这样整体状态量就可以节省很多了。
    4. 滑动窗口pane复用

      1. 将窗口的状态划分成更小粒度的pane,比如上面3小时窗口、1小时滑动的情况,可以把pane设置为1h,这样每来一条数据,我们就只更新这条数据对应的pane的结果就可以了。当窗口需要输出结果的时候,只需要将这个窗口对应的pane的结果merge起来就可以了