Spark Streaming 的 reduceByKeyAndWindow 窗口函数

本文深入探讨了Spark Streaming中的窗口计算功能,包括窗口函数的基本概念、常见操作如countByWindow和reduceByKeyAndWindow的详细解释,以及如何通过重载方法提高计算效率。通过具体案例,展示了如何使用Spark Streaming进行实时数据流处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spark Streaming提供了窗口计算,可让您在数据的滑动窗口上应用转换
官方SparkStreaming-window图
一些常见的窗口操作如下。所有这些操作都采用上述两个参数-windowLength(窗口长度)slideInterval(滑动步长)

窗口函数描述
window(windowLength,slideInterval)Return a new DStream which is computed based on windowed batches of the source DStream.
countByWindow(windowLength,slideInterval)Return a sliding window count of elements in the stream.
reduceByKeyAndWindow(func,windowLength,slideInterval,[ numTasks ])Return a new single-element stream, created by aggregating elements in the stream over a sliding interval using func. The function should be associative and commutative so that it can be computed correctly in parallel.
reduceByKeyAndWindow(func,invFunc,windowLength, slideInterval,[ numTasks ])When called on a DStream of (K, V) pairs, returns a new DStream of (K, V) pairs where the values for each key are aggregated using the given reduce function func over batches in a sliding window. Note: By default, this uses Spark’s default number of parallel tasks (2 for local mode, and in cluster mode the number is determined by the config property spark.default.parallelism) to do the grouping. You can pass an optional numTasks argument to set a different number of tasks.
countByValueAndWindow(windowLength, slideInterval,[ numTasks ])When called on a DStream of (K, V) pairs, returns a new DStream of (K, Long) pairs where the value of each key is its frequency within a sliding window. Like in reduceByKeyAndWindow, the number of reduce tasks is configurable through an optional argument.

reduceByKeyAndWindow 窗口函数有两个重载方法

一般情况下,我们会写

//reduceByKeyAndWindow(func,windowLength,slideInterval,[ numTasks ])

val df = socketDStream // 从socket 得到值
          .flatMap(_.split(" "))
          .map((_, 1))
          .reduceByKeyAndWindow(
              (_: Int) + (_: Int),
              Seconds(3),
              Seconds(2))

也就是最上面官方给出的那种图,每 2 秒,统计前3秒的数据(当前时间点的前3秒),此时此刻我们看到,当前窗口和前一个窗口重叠了一部分数据,导致 time3 会被计算两次,要想 time3不被计算两次,我们就要用到 reduceByKeyAndWindow 的重载方法:

reduceByKeyAndWindow(func,invFunc,windowLength, slideInterval,[ numTasks ])

我们先看看官方给出的函数描述,翻译过来就是说:

该重载方法比 reduceByKeyAndWindow(func,windowLength,slideInterval,[ numTasks ]) 更加高效

原因是,其中每个窗口的 reduce 值,使用前一个窗口的 reduce 值递增计算,得到当前前窗口的 reduce 值,然后减去 前一个窗口 失效的值

也就是说,计算规则变成了:

window1 = time1 + time2 + time3
window2 = window1 + time3 + time 4 - time1 - time2 => time3 + time4 + time5

那么实现起来就是

val df1 = socketDStream
          .flatMap(_.split(" "))
          .map((_, 1))
          .reduceByKeyAndWindow(
              (_: Int) + (_: Int),
              (_: Int) - (_: Int),
              Seconds(3),
              Seconds(2))

其中第一个函数值 _+_ 是 对滑动后窗口中新的数据(time4, time5)进行累加统计
第二个函数 _-_是对滑动后,前一个窗口过时的数据(time1,time2)进行减去统计


看一节完整案例:从 socket 接受数据做 wordCount

package apache

import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.dstream.ReceiverInputDStream

/**
  * @author kino
  * @version 1.0.0
  */
object ReduceByKeyWindow2 {
    def main(args: Array[String]): Unit = {
        val ssc = new StreamingContext(
            new SparkConf()
              .setMaster("local[2]")
              .setAppName("reduceByWindow"),
            Seconds(1))
        ssc.checkpoint("./reduceByKeyWindowPoint")

        val socketDStream: ReceiverInputDStream[String] = ssc.socketTextStream("hadoop102", 9999)

        val df = socketDStream
          .flatMap(_.split(" "))
          .map((_, 1))
          .reduceByKeyAndWindow(
              (_: Int) + (_: Int),
              Seconds(3),
              Seconds(2))

        val df1 = socketDStream
          .flatMap(_.split(" "))
          .map((_, 1))
          .reduceByKeyAndWindow(
              (_: Int) + (_: Int),
              (_: Int) + (_: Int),
              Seconds(3),
              Seconds(2))

        df.print
        df1.print
        ssc.start
        ssc.awaitTermination
    }
}
### Spark Streaming 支持窗口函数及其用法 #### 窗口函数的支持情况 Spark Streaming 完全支持窗口函数,这使得处理基于时间间隔的数据流变得简单有效。通过这些功能可以执行诸如聚合、过滤和其他复杂操作。 #### 参数解释 对于 `DStream` 而言,在应用窗口变换时涉及到两个重要参数:`windowDuration` 和 `slideDuration`。前者定义了窗口的总长度;后者则指定了每隔多久滑动一次窗口来创建新的批次[^1]。 #### 函数具体实现方式 以 `reduceByKeyAndWindow` 方法为例,此方法允许开发者指定用于更新状态值的操作——即当有新数据到来时应如何将其加入现有记录之中(`reduceFunc`) ,同时也提供了移除旧数据项的能力 (`invReduceFunc`) 。该过程涉及四个主要组成部分: - **reduceFunc**: 对新增加的数据片段进行规约运算。 - **invReduceFunc**: 当某些键对应的条目超出了当前活动窗口范围,则利用这个逆向减少函数去除那些不再属于最新窗口内的元素。 - **windowDuration**: 设定整个分析周期持续多长时间。 - **slideDuration**: 控制着每次向前推进多少秒形成下一个待处理区间[^2]。 下面给出一段简单的代码示例展示怎样运用上述概念构建一个每三秒钟汇总一次单词计数的应用程序,并且每一秒都会触发一轮计算: ```scala val words = socketDStream.flatMap(_.split(" ")) val pairs = words.map(word => (word, 1)) pairs.reduceByKeyAndWindow( (a: Int, b: Int) => a + b, Seconds(3), Seconds(1) ).print() ``` 这段脚本首先将输入字符串分割成单独词语并映射为二元组形式 `(word, count)`,接着调用了带有特定窗口配置的 `reduceByKeyAndWindow()` 来累积各词频次,最后打印结果至控制台[^3]。 值得注意的是,在实际应用场景下可能还需要考虑更多细节设置比如分区数目调整等优化措施以提高性能表现。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值