在Spark Streaming中,DStream的转换分为有状态和无状态两种。无状态的操作,即当前批次的处理不依赖于先前批次的数据,如map()、flatMap()、filter()、reduceByKey()、groupByKey()等等;而有状态的操作,即当前批次的处理需要依赖先前批次的数据,这样的话,就需要跨批次维护状态。

总结spark streaming中的状态操作:updateStateByKey、mapWithState

updateStateByKey


对整个实时计算的所有时间间隔内产生的相关数据进行统计。 spark streaming 的解决方案是累加器,工作原理是定义一个类似全局的可更新的变量,每个 时间窗口内得到的统计值都累加到上个时间窗口得到的值,这样整个累加值就是跨越多个时间间隔。


updateStateByKey 操作可以让我们为每个 key 维护一份 state ,并持续不断的更新该 state 。


  • 首先,要定义一个 state,可以是任意的数据类型;
  • 其次,要定义 state 更新函数(指定一个函数如何使用之前的 state 和新值来更新 state)。 对于每个 batch,spark 都会为每个之前已经存在的 key 去应用一次 state 更新函数,无论这个 key 在 batch 中是否有新的数据。如果 state 更新函数返回 none,那么 key 对应的 state 就会被删除。 当然对于每个新出现的 key 也会执行 state 更新函数。

注意


updatestateBykey 要求必须开启 checkpoint 机制。


updateStateByKey 返回的都是 DStream 类型。根据 updateFunc 这个函数来更新状态。其中参数:Seq[V] 是本次的数据类型, Option[S] 是前次计算结果类型,本次计算结果类型也是 Option[S]。计算肯定需要 Partitioner 。因为 Hash 高效率且不做排序,默认 Partitioner 是 HashPartitoner。 由于 cogroup 会对所有数据进行扫描,再按 key 进行分组,所以性能上会有问题。特别是随着时间的推移,这样的计算到后面会越算越慢。


所以数据量大的计算、复杂的计算,都不建议使用 updateStateByKey 。


 


mapWithState

mapWithState从spark 1.6.0开始出现,可以看做是updateStateByKey的升级版,有一些updateStateByKey所没有的特征:

1、支持输出只发生更新的状态和全量状态
mapWithState默认每个批次只会返回当前批次中有新数据的Key的状态,也可返回全量状态。updateStateByKey每个批次都会返回所有状态。

2、内置状态超时管理
内置状态超时管理,可对超时的Key单独处理。也可实现如会话超时(Session Timeout)的功能。在updateStateByKey中,如果要实现类似功能,需要大量编码。

3、初始化状态
可以选择自定义的RDD来初始化状态。

4、可以返回任何我们期望的类型
由mapWithState函数可知,可以返回任何我们期望的类型,而updateStateByKey做不到这一点。

5、性能更高
实现上,mapWithState只是增量更新,updateStateByKey每个批次都会对历史全量状态和当前增量数据进行cogroup合并,状态较大时,性能较低。