暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

横向进阶 | Spark和Flink的状态管理State的区别和应用

实时数仓Flink 2021-11-22
913


关注我们, 一起成长!


首先区分一下两个概念,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)映射。

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。用户通过put
    putAll
    方法添加元素。


一个创建和使用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)

- END -

期待与大佬技术交流、思想碰撞!点个关注,交个朋友↓


猜你喜欢

Spark必知必会 | Spark SQL自定义函数UDF、UDAF聚合函数以及开窗函数的使用


数仓开发到底在开发什么?


Flink重点难点 | Flink SQL高效Top-N方案与原理



文章转载自实时数仓Flink,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论