Flink -- 状态管理

一、Flink 中的状态

1.1 概述

        Flink中算子任务可以分为有状态和无状态。

        无状态的算子任务只需要观察每个独立事件,根据当前输入的数据直接转换输出结果。比如map、flatmap

        有状态的算子,除了当前数据之外,还需要一些其他的数据来得到计算结果。这些其他数据,就是所谓的状态。比如聚合算子、窗口算子都属于有状态算子

 1.2 状态分类

1)托管状态

        由Flink 统一管,不需要我们管。比如存钱到银行,我们只负责存钱,不管银行用这笔钱干删么。托管状态又分为算子状态按键分区状态

        ①:算子状态 指 每个算子,可能有多个并行度,每个并行度维护自己的状态

        ②:按键分区状态 指 根据输入流中的key来维护和访问。一个子任务中按照key进行更细维护

2)原始状态

        是自定义的,相当于就是开辟了一块内存,需要我们自己管理,实现状态的序列化和故障恢复

二、按键分区状态

1)值状态

        状态中只保存一个"值"。ValueState<T>本身是一个接口,在open方法中初始化值。如果要用外部变量去存,为了达成按键分区的目的,需要维护HashMap,key为key

        案例需求:检测每种传感器的水位值,如果连续的两个水位值超过10,就输出报警

public class KeyedValueStateDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);


        SingleOutputStreamOperator<WaterSensor> sensorDS = env
                .socketTextStream("hadoop102", 7777)
                .map(new WaterSensorMapFunction())
                .assignTimestampsAndWatermarks(
                        WatermarkStrategy
                                .<WaterSensor>forBoundedOutOfOrderness(Duration.ofSeconds(3))
                                .withTimestampAssigner((element, ts) -> element.getTs() * 1000L)
                );

        sensorDS.keyBy(r -> r.getId())
                .process(
                        new KeyedProcessFunction<String, WaterSensor, String>() {

                            // TODO 1.定义状态
                            ValueState<Integer> lastVcState;


                            @Override
                            public void open(Configuration parameters) throws Exception {
                                super.open(parameters);
                                // TODO 2.在open方法中,初始化状态
                                // 状态描述器两个参数:第一个参数,起个名字,不重复;第二个参数,存储的类型
                                lastVcState = getRuntimeContext().getState(new ValueStateDescriptor<Integer>("lastVcState", Types.INT));
                            }

                            @Override
                            public void processElement(WaterSensor value, Context ctx, Collector<String> out) throws Exception {
//                                lastVcState.value();  // 取出 本组 值状态 的数据
//                                lastVcState.update(); // 更新 本组 值状态 的数据
//                                lastVcState.clear();  // 清除 本组 值状态 的数据


                                // 1. 取出上一条数据的水位值(Integer默认值是null,判断)
                                int lastVc = lastVcState.value() == null ? 0 : lastVcState.value();
                                // 2. 求差值的绝对值,判断是否超过10
                                Integer vc = value.getVc();
                                if (Math.abs(vc - lastVc) > 10) {
                                    out.collect("传感器=" + value.getId() + "==>当前水位值=" + vc + ",与上一条水位值=" + lastVc + ",相差超过10!!!!");
                                }
                                // 3. 更新状态里的水位值
                                lastVcState.update(vc);
                            }
                        }
                )
                .print();

        env.execute();
    }
}

2)列表状态 

将需要保存的数据,以列表的形式组织起来。ListState<T>,使用方式和一般的List非常相似

案例:针对每种传感器输出最高的3个水位值

public class KeyedListStateDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);


        SingleOutputStreamOperator<WaterSensor> sensorDS = env
                .socketTextStream("hadoop102", 7777)
                .map(new WaterSensorMapFunction())
                .assignTimestampsAndWatermarks(
                        WatermarkStrategy
                                .<WaterSensor>forBoundedOutOfOrderness(Duration.ofSeconds(3))
                                .withTimestampAssigner((element, ts) -> element.getTs() * 1000L)
                );

        sensorDS.keyBy(r -> r.getId())
                .process(
                        new KeyedProcessFunction<String, WaterSensor, String>() {

                            ListState<Integer> vcListState;

                            @Override
                            public void open(Configuration parameters) throws Exception {
                                super.open(parameters);
                                vcListState = getRuntimeContext().getListState(new ListStateDescriptor<Integer>("vcListState", Types.INT));
                            }

                            @Override
                            public void processElement(WaterSensor value, Context ctx, Collector<String> out) throws Exception {
                                // 1.来一条,存到list状态里
                                vcListState.add(value.getVc());

                                // 2.从list状态拿出来(Iterable), 拷贝到一个List中,排序, 只留3个最大的
                                Iterable<Integer> vcListIt = vcListState.get();
                                // 2.1 拷贝到List中
                                List<Integer> vcList = new ArrayList<>();
                                for (Integer vc : vcListIt) {
                                    vcList.add(vc);
                                }
                                // 2.2 对List进行降序排序
                                vcList.sort((o1, o2) -> o2 - o1);
                                // 2.3 只保留最大的3个(list中的个数一定是连续变大,一超过3就立即清理即可)
                                if (vcList.size() > 3) {
                                    // 将最后一个元素清除(第4个)
                                    vcList.remove(3);
                                }

                                out.collect("传感器id为" + value.getId() + ",最大的3个水位值=" + vcList.toString());

                                // 3.更新list状态
                                vcListState.update(vcList);


//                                vcListState.get();            //取出 list状态 本组的数据,是一个Iterable
//                                vcListState.add();            // 向 list状态 本组 添加一个元素
//                                vcListState.addAll();         // 向 list状态 本组 添加多个元素
//                                vcListState.update();         // 更新 list状态 本组数据(覆盖)
//                                vcListState.clear();          // 清空List状态 本组数据
                            }
                        }
                )
                .print();

        env.execute();
    }
}

3)map状态

类似于HashMap操作

4)规约状态

5)聚合状态

6)状态生存时间TTL

状态的失效其实不需要立即删除,而是可以给状态附加一个属性,也就是状态的“失效时间”。状态创建的时候,设置失效时间=当前时间+TTL。之后如果有对状态的访问和修改,可以对失效时间进行更新

三、算子状态      

1)列表状态

对于一个子任务,有一个列表,而按键分区中,一个子任务按照key分区,可能是多个列表

轮询均分给新的 并行子任务

2) 联合列表状态

原先的多个子任务的状态,合并成一份完整的。会把完整列表给新的子任务,每人一份完整的

3)广播状态

所有的并行子任务都保持一份全局状态,都访问一个,就像被广播一样

数据流只能读取广播状态,不能修改。只有广播流才能修改

四、状态后端

主要负责管理本地状态的存储方式和位置

1. 分类

1)哈希状态后端

是把状态放到内存中。在内部直接把状态当做对象,保存在TaskManager的JVM堆上。底层是一个哈希Map

2)RocksDB状态后端

kv型数据库,可以把数据持久化存储到本地磁盘,默认存储在TaskManager的本地数据目录中。且RocksDB的状态数据被存储为序列化的字节数组,读写操作需要序列化和反序列化

2. 如何选择状态后端

        哈希状态后端是将状态放入内存,速度比较快,但是缺点是用TaskManager的内存,状态大小受集群可用内存的限制

        RocksDB是硬盘存储,且需要序列化反序列化,性能较差

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值