Flink-State

前言

image-20210529114106141

从官网中,我们可以得知,Flink是一个可针对于有限流与无限流数据做有状态计算的处理引擎。


什么叫无状态计算

计算过程不需要考虑历史数据历史结果,相同的输入得到相同的输出,每一次计算都是独立的

ex:flink中的map算子 、FlatMap算子等等

什么叫有状态计算

有状态计算就是指,我们对一个数据计算过程中,可以拿到上一次计算的结果,在上一次计算结果中进行累积或逻辑处理。

比如,计算一个小时内用户成交总金额,那么Flink在计算一个小时内的用户支付信息的时候,就需要对支付信息进行一个不断的累积,然后输出最后的计算结果。

Ex:现在一个小时内用户成交信息有五条,其中每条中金额如下

1 2 1 3 4

那么一个小时内的总金额则为1+2+1+3+4

那么,如果想要不断的数据进行累积,我们要做的便是对数据进行状态管理,因为既然要累积,那自然要找个地方存储计算结果,以便于下一个元素来到时可以取出上一次计算结果进行累加处理

这样一来,便涉及到了中间状态(上一次结果)的管理维护了!

自己设计的状态管理

(1)内存管理,使用原生java工具包 Map、List等

flink集群部署下存在问题

(2)借助第三方组件 Redis

频繁需要与Redis交互(频繁网络IO)

Flink中的有状态计算

Flink中已经对需要进行有状态计算的API,做了封装,底层已经维护好了状态!

示例

一起回到梦开始的地方=========Flink-World-Count

package com.leilei;

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;

/**
 * @author lei
 * @version 1.0
 * @date 2021/3/7 15:51
 * @desc 单词计数 DataStream 匿名内部类
 */
public class WordCountDataStream1 {
    public static void main(String[] args) throws Exception {
        // 1.准备环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        // 设置运行模式
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);
        // 2.准备数据源
        DataStreamSource<String> elementsSource = env.fromElements(
                "java,scala,php,c++",
                "java,scala,php", "java,scala", "java");
        // 3.数据处理转换
        KeyedStream<Tuple2<String, Integer>, String> streamResult = elementsSource.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public void flatMap(String element, Collector<String> out) throws Exception {
                String[] wordArr = element.split(",");
                for (String word : wordArr) {
                    out.collect(word);
                }
            }
        }).map(new MapFunction<String, Tuple2<String, Integer>>() {
            @Override
            public Tuple2<String, Integer> map(String word) throws Exception {
                return Tuple2.of(word, 1);
            }
        }).keyBy(new KeySelector<Tuple2<String, Integer>, String>() {
            @Override
            public String getKey(Tuple2<String, Integer> value) throws Exception {
                return value.f0;
            }
        });
        SingleOutputStreamOperator<Tuple2<String, Integer>> sum = streamResult.sum(1);
        // 4.数据输出
        sum.print();
        // 5.执行程序
        env.execute("flink-hello-world");

    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OH8dixra-1623161719085)(C:\Users\leile\AppData\Roaming\Typora\typora-user-images\image-20210607210126177.png)]

Flink自动为我们对数据做了状态累计, java这个词出现了4次、scala出现了三次…

为什么 Flink 知道之前已经处理过一次 java 或者scala了呢?实际上这里是state 发挥作用了,在这里,数据被称为 keyed state 存储了计算结果,当有了相同数据时,在对应结果中继续进行数据累计

Flink状态分类

Managed State & Raw State

从状态的托管类型(是否由Flink来管理)来区分,Flink分为ManagedState 和Raw State

ManagedState :

Managed State (托管状态):由 Flink Runtime 管理,自动存储,自动恢复,在内存管理上有优化,Managed State 支持已知的数据结构,如 Value、List、Map 等等,在大多数场景下均可使用

RawState:

RawState(原始状态):需要用户自己管理,需要自己序列化,Flink 不知道 State 中存入的数据是什么结构,只有用户自己知道,需要最终序列化为可存储的数据结构,数据结构只支持字节数组 ,所有状态都要转换为二进制字节数组;一般作用在当 Managed State 不够用时,比如需要自定义 Operator 时,才会使用 Raw State


在实际生产环境中,我们一般都是由Flink来为我们做状态管理,我们只需要测住关注自己的计算逻辑(即大多数情况下使用的是托管状态ManagedState)

Managed State 分为两种,Keyed State 和 Operator State

image-20210608213546353

Keyed State(键控状态)

Keyed State基于KeyedStream(数据流使用keyBy()算子)基础上才可使用。

Keyed State由数据流中定义的每个具体的键(key) 来维护和访问,每个key维护一个状态实例,flink会将具有相同键的数据分区到同一个算子任务中,这个任务会维护和处理这个key对应的状态(会自动将状态的访问范围限定为当前数据的key,一个key只能访问它自己的状态,不同key之间也不能互相访问) 当任务处理一条数据时,它会自动将状态的访问范围限定为当前数据的key

键控状态Keyed State 数据结构:由于每个键属于 keyed operator 的一个并行实例,可将其简单地视为 <parallel-operator-instance, key>

image-20210608214123039

键控状态Keyed State 数据结构:

① 值状态(ValueState),将状态表示为单个值;(直接.value获取,Set操作是.update)

  • get操作: ValueState.value()
  • set操作: ValueState.update(T value)

② 列表状态(ListState),将状态表示为一组数据的列表(存多个状态);(.get,.update,.add)

  • ListState.add(T value)
  • ListState.addAll(List values)
  • ListState.get()返回Iterable
  • ListState.update(List values)

③ 映射状态(MapState<K, V>),将状态表示为一组Key-Value对;(.get,.put ,类似HashMap)

  • MapState.get(UK key)
  • MapState.put(UK key, UV value)
  • MapState.contains(UK key)
  • MapState.remove(UK key)

④ 聚合状态(ReducingState & AggregatingState<I, O>),将状态表示为一个用于聚合操作的列表;(.add不像之前添加到列表,它是直接聚合到之前的结果中)

Operator State(算子状态)

作用范围限定为算子任务,同一任务状态共享(每一个并行的子任务共享一个状态),由同一并行任务所处理的所有数据都可以访问到相同的状态,算子状态不能由相同或不同算子的另一个任务访问(相同算子的不同任务之间也不能访问

算子状态提供三种数据结构:

① 列表状态(List state),将状态表示为一组数据的列表;(会根据并行度的调整把之前的状态重新分组重新分配

② 联合列表状态(Union list state),也将状态表示为数据的列表,它常规列表状态的区别在于,在发生故障时,或者从保存点(savepoint)启动应用程序时如何恢复(把之前的每一个状态广播到对应的每个算子中)。

③ 广播状态(Broadcast state),如果一个算子有多项任务,而它的每项任务状态又都相同,那么这种特殊情况最适合应用广播状态(把同一个状态广播给所有算子子任务);

image-20210608214201770

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值