Spark和Flink的状态管理State的区别和应用

640?wx_fmt=jpeg

点击右侧关注,大数据开发领域最强公众号!
640?wx_fmt=jpeg

640?wx_fmt=png

点击右侧关注,暴走大数据!
640?wx_fmt=png



By  大数据技术与架构

场景描述:如果一个task在处理过程中挂掉了,那么它在内存中的状态都会丢失,所有的数据都需要重新计算。那么我就需要一个东西保存历史状态State。

关键词:State Flink Spark

首先区分一下两个概念,state一般指一个具体的task/operator的状态。而checkpoint则表示了一个Job,在一个特定时刻的一份全局状态快照,即包含了所有task/operator的状态。我们在这里讨论的是state。

 

Spark的状态更新

updateStateByKey

updateStateByKey会统计全局的key的状态,不管又没有数据输入,它会在每一个批次间隔返回之前的key的状态。updateStateByKey会对已存在的key进行state的状态更新,同时还会对每个新出现的key执行相同的更新函数操作。如果通过更新函数对state更新后返回来为none,此时刻key对应的state状态会被删除(state可以是任意类型的数据的结构)。

mapWithState

mapWithState也会统计全局的key的状态,但是如果没有数据输入,便不会返回之前的key的状态,类似于增量的感觉。

updateStateByKey和mapWithState的区别

updateStateByKey可以在指定的批次间隔内返回之前的全部历史数据,包括新增的,改变的和没有改变的。由于updateStateByKey在使用的时候一定要做checkpoint,当数据量过大的时候,checkpoint会占据庞大的数据量,会影响性能,效率不高。

mapWithState只返回变化后的key的值,这样做的好处是,我们可以只是关心那些已经发生的变化的key,对于没有数据输入,则不会返回那些没有变化的key的数据。这样的话,即使数据量很大,checkpoint也不会像updateStateByKey那样,占用太多的存储,效率比较高(再生产环境中建议使用这个)。

updateStateByKey示例:

def updateFunction(currValues:Seq[Int],preValue:Option[Int]): Option[Int] = {	
       val currValueSum = currValues.sum	
        //上面的Int类型都可以用对象类型替换	
        Some(currValueSum + preValue.getOrElse(0)) //当前值的和加上历史值	
    }	
    kafkaStream.map(r => (r._2,1)).updateStateByKey(updateFunction _)

这里的updateFunction方法就是需要我们自己去实现的状态跟新的逻辑,currValues就是当前批次的所有值,preValue是历史维护的状态,updateStateByKey返回的是包含历史所有状态信息的DStream。

mapWithState示例:

val initialRDD = ssc.sparkContext.parallelize(List[(String, Int)]())	
    //自定义mappingFunction,累加单词出现的次数并更新状态	
   val mappingFunc = (word: String, count: Option[Int], state: State[Int]) => {	
   val sum = count.getOrElse(0) + state.getOption.getOrElse(0)	
   val output = (word, sum)	
   state.update(sum)	
   output	
    }	
    //调用mapWithState进行管理流数据的状态	
  kafkaStream.map(r => (r._2,1)).mapWithState(StateSpec.function(mappingFunc).initialState(initialRDD)).print()

这里的initialRDD就是初始化状态,updateStateByKey也有对应的API。这里的mappingFun也是需要我们自己实现的状态跟新逻辑,调用state.update()就是对状态的跟新,output就是通过mapWithState后返回的DStream中的数据形式。注意这里不是直接传入的mappingFunc函数,而是一个StateSpec 的对象,其实也是对函数的一个包装而已。


 

Flink的状态更新

Flink中包含两种基础的状态:Keyed State和Operator State。

Keyed State

顾名思义,就是基于KeyedStream上的状态。这个状态是跟特定的key绑定的,对KeyedStream流上的每一个key,可能都对应一个state。

Operator State

与Keyed State不同,Operator State跟一个特定operator的一个并发实例绑定,整个operator只对应一个state。相比较而言,在一个operator上,可能会有很多个key,从而对应多个keyed state。

举例来说,Flink中的Kafka Connector,就使用了operator state。它会在每个connector实例中,保存该实例中消费topic的所有(partition, offset)映射。

640?wx_fmt=pngstate的整个继承关系

Keyed State

首先看一下Keyed State下,我们可以用哪些原子状态:

  • ValueState:即类型为T的单值状态。这个状态与对应的key绑定,是最简单的状态了。它可以通过update方法更新状态值,通过value()方法获取状态值。

  • ListState:即key上的状态值为一个列表。可以通过add方法往列表中附加值;也可以通过get()方法返回一个Iterable<T>来遍历状态值。

  • ReducingState:这种状态通过用户传入的reduceFunction,每次调用add方法添加值的时候,会调用reduceFunction,最后合并到一个单一的状态值。

  • FoldingState:跟ReducingState有点类似,不过它的状态值类型可以与add方法中传入的元素类型不同(这种状态将会在Flink未来版本中被删除)。

  • MapState:即状态值为一个map。用户通过putputAll方法添加元素。

一个创建和使用ValueState的例子:

public class CountWindowAverage extends RichFlatMapFunction<Tuple2<Long, Long>, Tuple2<Long, Long>> {	

	
    /**	
     * ValueState状态句柄. 第一个值为count,第二个值为sum。	
     */	
    private transient ValueState<Tuple2<Long, Long>> sum;	

	
    @Override	
    public void flatMap(Tuple2<Long, Long> input, Collector<Tuple2<Long, Long>> out) throws Exception {	
        // 获取当前状态值	
        Tuple2<Long, Long> currentSum = sum.value();	

	
        // 更新	
        currentSum.f0 += 1;	
        currentSum.f1 += input.f1;	

	
        // 更新状态值	
        sum.update(currentSum);	
        	
        // 如果count >=2 清空状态值,重新计算	
        if (currentSum.f0 >= 2) {	
            out.collect(new Tuple2<>(input.f0, currentSum.f1 / currentSum.f0));	
            sum.clear();	
        }	
    }	

	
    @Override	
    public void open(Configuration config) {	
        ValueStateDescriptor<Tuple2<Long, Long>> descriptor =	
                new ValueStateDescriptor<>(	
                        "average", // 状态名称	
                        TypeInformation.of(new TypeHint<Tuple2<Long, Long>>() {}), // 状态类型	
                        Tuple2.of(0L, 0L)); // 状态默认值	
        sum = getRuntimeContext().getState(descriptor);	
    }	
}	

	
// ...	
env.fromElements(Tuple2.of(1L, 3L), Tuple2.of(1L, 5L), Tuple2.of(1L, 7L), Tuple2.of(1L, 4L), Tuple2.of(1L, 2L))	
        .keyBy(0)	
        .flatMap(new CountWindowAverage())	
        .print();	

	
// the printed output will be (1,4) and (1,5)

欢迎点赞+收藏+转发朋友圈素质三连

640?wx_fmt=jpeg640?wx_fmt=jpeg

文章不错?点个【在看】吧! ?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王知无(import_bigdata)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值