Spark 函数 updateStateByKey 解析

updateStateByKey 操作允许您在使用新的信息持续更新时保持任意状态
1、定义状态 - 状态可以是任意数据类型。 
2、定义状态更新功能 - 使用函数指定

例如
以 DStream 中的数据进行按 key 做 reduce 操作,然后对各个批次的数据进行累加 
对于有状态操作,要不断的把当前和历史的时间切片的 RDD 累加计算,随着时间的流逝⌛,计算的数据规模会变得越来越大

updateStateBykey 函数有 6 种重载函数

1、只传入一个更新函数,最简单的一种

updateFunc: (Seq[V], Option[S]) => Option[S]): DStream[(K, S)] = ssc.withScope {
    updateStateByKey(updateFunc, defaultPartitioner())
}

例如,对于 wordcount,我们可以这样定义更新函数:

(values:Seq[Int],state:Option[Int])=>{

// 创建一个变量,用于记录单词出现次数

var newValue=state.getOrElse(0) //getOrElse 相当于 if....else.....

for(value <- values){

    newValue +=value // 将单词出现次数累计相加

}

Option(newValue)

}

2、传入更新函数和分区数

def updateStateByKey[S: ClassTag](

updateFunc: (Seq[V], Option[S]) => Option[S],numPartitions: Int): DStream[(K, S)] = ssc.withScope {

     updateStateByKey(updateFunc, defaultPartitioner(numPartitions))

}

3、传入更新函数和自定义分区

def updateStateByKey[S: ClassTag](

updateFunc: (Seq[V], Option[S]) => Option[S],partitioner: Partitioner): DStream[(K, S)] = ssc.withScope {

    val cleanedUpdateF = sparkContext.clean(updateFunc)

    val newUpdateFunc = (iterator: Iterator[(K, Seq[V], Option[S])]) => {

        iterator.flatMap(t => cleanedUpdateF(t._2, t._3).map(s => (t._1, s)))

    }

    updateStateByKey(newUpdateFunc, partitioner, true)

}

4、传入完整的状态更新函数

前面的函数传入的都是不完整的更新函数,只是针对一个 key 的,他们在执行的时候也会生成一个完整的状态更新函数。

Iterator[(K, Seq[V], Option[S])]) => Iterator[(K, S)] 入参是一个迭代器,参数 1 是 key,参数 2 是这个 key 在这个 batch 中更新的值的集合,参数 3 是当前状态,最终得到 key-->newvalue

def updateStateByKey[S: ClassTag](

updateFunc: (Iterator[(K, Seq[V], Option[S])]) => Iterator[(K, S)],partitioner: Partitioner,rememberPartitioner: Boolean): DStream[(K, S)] = ssc.withScope {

    new StateDStream(self, ssc.sc.clean(updateFunc), partitioner, rememberPartitioner, None)

}

例如,对于 wordcount:

val newUpdateFunc = (iterator: Iterator[(String, Seq[Int], Option[Int])]) => {

    iterator.flatMap(t => function1(t._2, t._3).map(s => (t._1, s)))

}

5、加入初始状态

initialRDD: RDD[(K, S)] 初始状态集合

def updateStateByKey[S: ClassTag](

updateFunc: (Seq[V], Option[S]) => Option[S],

partitioner: Partitioner,

initialRDD: RDD[(K, S)]

): DStream[(K, S)] = ssc.withScope {

val cleanedUpdateF = sparkContext.clean(updateFunc)

val newUpdateFunc = (iterator: Iterator[(K, Seq[V], Option[S])]) => {

iterator.flatMap(t => cleanedUpdateF(t._2, t._3).map(s => (t._1, s)))

}

updateStateByKey(newUpdateFunc, partitioner, true, initialRDD)

}

6、是否记得当前的分区

def updateStateByKey[S: ClassTag](

updateFunc: (Iterator[(K, Seq[V], Option[S])]) => Iterator[(K, S)],

partitioner: Partitioner,

rememberPartitioner: Boolean,

initialRDD: RDD[(K, S)]

): DStream[(K, S)] = ssc.withScope {

new StateDStream(self, ssc.sc.clean(updateFunc), partitioner,

rememberPartitioner, Some(initialRDD))

}

 

wordcount 例子:

def testUpdate={

val sc = SparkUtils.getSpark("test", "db01").sparkContext

val ssc = new StreamingContext(sc, Seconds(5))

ssc.checkpoint("hdfs://ns1/config/checkpoint")

val initialRDD = sc.parallelize(List(("hello", 1), ("world", 1)))

val lines = ssc.fileStream[LongWritable,Text,TextInputFormat]("hdfs://ns1/config/data/")

val words = lines.flatMap(x=>x._2.toString.split(","))

val wordDstream :DStream[(String, Int)]= words.map(x => (x, 1))

val result=wordDstream.reduceByKey(_ + _)

 

def function1(newValues: Seq[Int], runningCount: Option[Int]): Option[Int] = {

val newCount = newValues.sum + runningCount.getOrElse(0) // add the new values with the previous running count to get the new count

Some(newCount)

}

val newUpdateFunc = (iterator: Iterator[(String, Seq[Int], Option[Int])]) => {

iterator.flatMap(t => function1(t._2, t._3).map(s => (t._1, s)))

}

val stateDS=result.updateStateByKey(newUpdateFunc,new HashPartitioner (sc.defaultParallelism),true,initialRDD)

stateDS.print()

ssc.start()

ssc.awaitTermination()

}

你可能感兴趣的:(Spark)