7、Flink中的状态

本文深入探讨了Apache Flink中的状态管理,重点讲解了算子状态和键控状态的概念、数据结构以及使用案例。算子状态局限于任务内部,而键控状态根据key进行分区,提供了更为灵活的数据访问。此外,还介绍了状态后端的选择,如MemoryStateBackend、FsStateBackend和RocksDBStateBackend,以及它们在容错和性能上的特点。

一、Flink中的状态

在这里插入图片描述
1)由一个任务维护,并且用来计算某个结果的所有数据,都属于这个任务的状态。
2)可以认为状态就是一个本地变量(一般放在本地内存,本地内存读取修改什么的都比较快),可以被任务的业务逻辑访问。
3)Flink会进行状态管理,包括状态一致性、故障处理以及高效存储和访问,以便开发人员可以专注于应用程序的逻辑。

像map、filter、flatMap这些算子,来一个输出一个,不依赖于其它的数据,也不依赖于之前的结果,所以它们是无状态的算子。
像window、reduce、聚合等一些操作,需要依赖于之前计算的结果,所以这些算子都属于有状态的算子。

在Flink中,状态始终与特定算子相关联(其实,在map、filter里面可以定义状态),跟特定的任务绑定在一起的,后面发生的任务不能访问到前面任务的状态,因为后面任务可能跟前面任务不在一个taskManager或者slot,如果要访问状态,需要做网络传输,而状态是在内存中的,不可能做网络传输。
为了使运行时的Flink了解算子的状态,算子需要预先注册其状态
总的来说,有两种类型的状态:
算子状态(Operator State):算子状态的作用范围限定为算子任务
键控状态(Keyed State):根据输入数据流中定义的键(key)来维护和访问

1、算子状态

在这里插入图片描述
上图中两个Task1属于一个算子的两个并行子任务,它们不在一个slot上,甚至不在一个TaskManager上,所以不能访问别人的状态。
1)算子状态的作用范围限定为算子任务,由同一并行任务(上图上面的一个Task1属于一个并行子任务,下面那个也是一个并行子任务,所以有两个并行子任务)所处理的所有数据都可以访问到相同的状态。
2)状态对于同一子任务而言是共享的(一个Task1里所有数据都共享这个状态)。
3)算子状态不能由相同或不同算子的另一个子任务访问即使上图中的两个Task1是一个算子的两个子任务,也不能互相访问)。
4)只要在同一个分区,不管key相不相同,访问的都是一个状态。

1.1 算子状态数据结构

1)列表状态(List state)
将列表表示为一组数据的列表(在故障恢复之后,可能会发生并行度的调整,如果要进行聚合还好说,如果要拆分就不容易去拆分)
2)联合列表状态(Union list state)
也将状态表示为数据的列表。它与常规列表状态的区别在于,在发生故障时,或者从保存点(savepoint)启动应用程序时如何恢复。
在这里插入图片描述
例如有两个并行子任务,每个并行子任务有三个状态,经过故障恢复后要并行度变为3,如果是列表状态,会把第一个子任务的前两个状态分给分区后的1,会把第二个子任务的前两个状态分给分区后的2,会把剩下的状态分给分区后的3;如果是联合列表状态,会把这六个状态给下游全部分发一份,让它们自己挑选状态。
3)广播状态(Broadcast state)
如果一个算子有多项任务,而它的每项任务状态又都相同,那么这种特殊情况最适合应用广播状态。

1.2 算子状态案例

需求:定义一个有状态的map操作,统计当前分区数据个数
代码如下:
下面代码可以实现一个有状态的map操作,可以统计当前分区内数据个数,但是如果不做特殊说明,容错的时候不会进行相应的处理,本地变量在内存中,没办法进行恢复,只能重新从0开始count。

    //定义一个有状态的map操作,统计当前分区的数据个数
    mapResult.map(new MapFunction<SensorReading, Integer>() {
   
   
        //定义一个本地变量作为算子状态
        private Integer count=0;

        @Override
        public Integer map(SensorReading sensorReading) throws Exception {
   
   
            count++;
            return count;
        }
    })

进行了容错配置的代码(还需要额外实现ListCheckpointed接口):

    mapResult.map(new MyCountMapper());

	public static class MyCountMapper implements MapFunction<SensorReading,Integer>, ListCheckpointed<Integer>{
   
   

        //定义一个本地变量作为算子状态
        private Integer count=0;

        @Override
        public Integer map(SensorReading sensorReading) throws Exception {
   
   
            count++;
            return count;
        }

        //Checkpoint触发时会调用这个方法,我们要实现具体的snapshot逻辑,比如将哪些本地状态持久化
        //对状态做快照,返回一个Integer的List
        @Override
        public List<Integer> snapshotState(long l, long l1) throws Exception {
   
   
            return Collections.singletonList(count);
        }

        //从上次Checkpoint中恢复数据到本地内存
        //发生故障时,从做的快照里面进行恢复count
        //如果发生并行度减少,可能l
在 Apache Flink 中,状态(State)是指任务在处理流数据时所维护的、用于计算结果的中间数据。这些数据可以被任务的业务逻辑访问和更新,并且在流处理过程中持续存在。状态管理是 Flink 实现有状态流处理的核心机制之一,它使得任务能够根据当前输入数据以及之前处理的数据共同生成输出结果。例如,在求和操作中,状态用于保存当前累计的和;在窗口操作中,状态用于保存窗口内的所有数据[^2]。 Flink 提供了多种状态类型,主要包括以下几种: 1. **Keyed State** Keyed State 是与特定的键(Key)绑定的状态,适用于基于键的流处理场景。每个键对应一个独立的状态实例,Flink 会根据键自动分区和管理状态。常见的 Keyed State 类型包括: - `ValueState<T>`:保存单个值的状态。 - `ListState<T>`:保存一个列表形式的状态。 - `MapState<K, V>`:保存键值对形式的状态。 2. **Operator State** Operator State 是与算子实例绑定的状态,不依赖于特定的键。它适用于需要在整个数据流中共享状态的场景,例如 Kafka 消费者的偏移量管理。 3. **Broadcast State** Broadcast State 用于广播流处理场景,所有算子实例都可以访问相同的状态数据。这种状态类型通常用于将配置信息广播到所有任务实例中。 Flink状态管理还支持检查点(Checkpoint)机制,确保状态在发生故障时能够恢复。通过定期将状态快照写入持久化存储,Flink 可以在任务失败时恢复到最近一次检查点的状态,从而实现“精确一次”(Exactly-Once)的语义保证。 ### 状态在流处理中的应用场景 状态在流处理中有广泛的应用,以下是一些典型场景: 1. **聚合操作** 在进行求和、平均值、最大值/最小值等聚合操作时,状态用于保存中间计算结果。例如,使用 `sum()` 或 `reduce()` 算子时,Flink 会自动维护相应的状态。 2. **窗口处理** 窗口操作(如滚动窗口、滑动窗口、会话窗口)依赖状态来保存窗口内的数据。当窗口触发计算时,状态中的数据会被处理并生成结果。 3. **事件时间处理** 在基于事件时间(Event Time)的处理中,状态用于保存尚未触发计算的数据,例如在使用水印(Watermark)机制处理乱序事件时。 4. **状态ful 转换操作** 自定义的 `ProcessFunction` 或 `RichFlatMapFunction` 等状态ful 算子可以显式地管理状态,以实现复杂的业务逻辑。 以下是一个使用 `ValueState` 的简单示例,展示如何在 Flink 中维护状态: ```java public class SumWithState extends RichFlatMapFunction<Integer, Integer> { private transient ValueState<Integer> sumState; @Override public void open(Configuration parameters) { sumState = getRuntimeContext().getState(new ValueStateDescriptor<>("sum", Integer.class, 0)); } @Override public void flatMap(Integer value, Collector<Integer> out) { int currentSum = sumState.value(); currentSum += value; sumState.update(currentSum); out.collect(currentSum); } } ``` 在上述代码中,`sumState` 用于保存当前的累加和,每次输入数据都会更新状态,并输出当前的累加结果[^2]。 ### 状态管理的优势 Flink状态管理机制具有以下优势: - **高效的状态访问**:Flink 提供了低延迟的状态访问能力,确保流处理任务的高性能。 - **容错能力**:通过检查点机制,Flink 能够在任务失败时恢复状态,从而保证数据处理的可靠性。 - **可扩展性**:Flink状态管理支持水平扩展,能够在大规模数据处理场景下保持良好的性能。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值