Flink——状态管理

什么是状态

在流处理中,我们需要处理的数据是源源不断的,那我们面对以下几种情况时该怎么办?

  • 从kakfa里面处理数据,但是kafak里的数据有些是重复的,需要在流处理系统里面进行去重,所以需要知道已经有的数据的id,那我们怎么知道呢
  • 需要与以前的历史数据进行比较等操作,但是又不想每次都到数据库里面去查(考虑到性能),那我们上哪里去读呢

强大的Flink提供了状态管理这么一个东西,可以让我们保存一些状态

我们想要什么样的状态管理

对于一个靠谱的流处理系统,需要满足以下几种要求

  • 7*24 小时运行,高可靠;
  • 数据不丢不重,恰好计算一次;
  • 数据实时产出,不延迟;

再看一下Flink官网对Flink状态管理的描述
在这里插入图片描述
没错,这就是我们想要的状态管理

Flink状态

Flink有两种基本的状态:Keyed StateOperator State,而这两种状态又有两种形式,即托管状态原始状态,本文主要介绍Keyed State的托管状态

  • 托管状态: 由flink运行时的数据结构表示的,支持多种数据结构,并且可以将这些数据结构写入检查点checkpoint,这也是flink官方推荐使用的
  • 原始状态:由用户自己定义的数据结构表示,Flink无法获取运行时的数据结构,只将字节序列写入检查点checkpoint
    在这里插入图片描述

Operator State

Operator State 可以用于所有算子,相对于直接从数据源拿到的数据更加友好。

支持的数据结构相对较少,如 ListState。

在改变并发度时,由于Operator State没有key,需要选择状态如何去重新分配,有两种该分配方式,分别为均匀分配将所有的State合并在分发给每个实例。

Keyed State

顾名思义,这个Keyed State与key有关,只能在KeyedStream上的函数和操作符中使用。

可以把Keyed State看成已经分组的状态,对于每一个key,都有一个状态分区,即在整个程序中没有 keyBy 的过程就没有办法使用 KeyedStream。

Keyed State支持更多常用的数据结构,比如:ValueState、ListState、ReducingState、AggregatingState 和 MapState

在改变并发度时,State 随着 Key 在实例间迁移,比如原来有 1 个并发,对应的 API 请求过来,/api/a 和 /api/b 都存放在这个实例当中;如果请求量变大,需要扩容,就会把 /api/a 的状态和 /api/b 的状态分别放在不同的节点

Keyed State的多种数据结构怎么用

在这里插入图片描述

  • ValueState 存储的是单个值,比如一个url的访问次数,有update和value两个方法,对应更新值和获取值
  • MapState 存储的是(K-V)型的状态,类似于java的Map接口,提供了put,get等方法
  • ListState 存储的是列表,类似于java中的list接口,提供add,update等方法
  • ReducingState 和 AggregatingState 与 ListState 都是同一个父类,但状态数据类型上是单个值,原因在于其中的 add 方法不是把当前的元素追加到列表中,而是把当前元素直接更新进了 Reducing 的结果中。
  • AggregatingState 的区别是在访问接口,ReducingState 中 add(T)和 T get() 进去和出来的元素都是同一个类型,但在 AggregatingState 输入的 IN,输出的是 OUT。

状态怎么进行存储?

Flink中状态的存储叫做状态后端,一共有三种状态后端。

  • MemoryStateBackend
  • FsStateBackend
  • RocksDBStateBackend

存储在内存中的MemoryStateBackend

状态和快照都会存储到jobManager的java 堆内存中,这也是系统默认的状态后端,也可以通过如下的代码显示的配置

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStateBackend(new MemoryStateBackend(MAX_MEM_STATE_SIZE, false));

有两个参数,第一个是内存中状态的最大容量,第二个是是否开启异步快照,官方建议开启异步,以避免保存状态快照时造成线程阻塞

限制

  • 每个状态默认为5M, 可以在构造函数中增大这个值,但是不能够超过 akka.framesize

适用场景

  • 本地开发或者调试。状态极少的作业

存储在文件系统中的FsStateBackend

状态仍然保存在内存中,而快照存储到配置的文件系统中,打破了每个状态大小的限制

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStateBackend(new FsStateBackend(path, false));

第一个参数是保存快照的文件系统的路径,第二个是是否开启异步保存快照。

限制:

  • 单个taskManager上的状态不能超过它的内存,快照总大小不能超过配置的文件系统大小

适用场景:

  • 状态较大,窗口较长,键/值状态较大的作业。
  • 所有高可用性设置。

存储在数据库的RocksDBStateBackend

RocksDB是一种嵌入式的本地key/value 的内存存储系统。状态通过RockDB存储到本地磁盘上,而快照存储到配置的文件系统中同时 Flink 会将极少的元数据存储在 JobManager 的内存中,或者在 Zookeeper 中(对于高可用的情况)。打破了taskManage的内存大小的限制,可以保存的状态的数量只受磁盘空间的限制。

需要注意RocksDBStateBackend不支持同步快照,并且支持增量的checkpoint,不用等每次状态满了再存储,有点像redis的aof?

使用RocksDBStateBackend需要加入相关的依赖

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-statebackend-rocksdb_2.11</artifactId>
    <version>1.11-SNAPSHOT</version>
    <scope>provided</scope>
</dependency>
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStateBackend(new RocksDBStateBackend());

限制:

  • RocksDB 支持的单 key 和单 value 的大小最大为每个 2^31 字节。这是因为 RocksDB 的 JNI API 是基于 byte[] 的。
  • 对于使用具有合并操作的状态的应用程序,例如 ListState,随着时间可能会累积到超过 2^31 字节大小,这将会导致在接下来的查询中失败。
    适用场景
  • 大状态,长窗口,或大键值状态的有状态处理任务
  • 高可用的需求
  • 超大状态,需要支持增量checkpoint的场景

发生故障时怎么恢复

Flink的状态存储是主要依靠 Checkpoint 机制,相当于redis的持久化一样,会定时的制作分布式快照,对程序中的状态进行备份,当发生错误时,从Checkpoint 恢复状态数据
在这里插入图片描述

状态的生存时间

流系统作业通常都是7*24不间断运行的,如果我们的所有状态都是永久存储的话,就算你财大气粗,也总有存储空间不足的那一天啊,所以就需要对存储的状态设置一个生存时间,过了这个生存时间之后,清除过期的状态。

Flink如何动态配置规则

通过广播状态配置规则

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值