Flink 快照分析:定位大状态和数据倾斜的算子

参照:Flink 快照分析:定位大状态和数据倾斜的算子 - 云+社区 - 腾讯云

在 Flink 作业中,无论是 SQL 还是 JAR 模式,常常会直接或者间接地使用到状态(State)。当 Flink 进行快照时,用户定义的这些状态数据可以被保存在状态点中,以供后续的崩溃恢复。
Flink 的状态分为 Operator State 和 Keyed State,而 Keyed State 又可以分为 ValueState、MapState、ListState、AggregatingState、MergingState、ReducingState 等多种类型。此外,这些林林总总的状态又有多种具体的实现(HeapState、RocksDBState 等),状态的存取还需要各种 Serializer 和 Deserializer 的参与,整个链路精妙而又繁杂。
对于普通用户而言,Flink 内部的运行模式就像黑盒,但是状态带来的困扰却是实实在在的,尤其是在使用 SQL 的多表 JOIN 或者 GROUP BY 等语义时,很容易因为状态越来越多,导致频繁的 TaskManager OOM(内存不足),影响线上业务的稳定性,更影响心情  ╮(╯_╰)╭
很多用户面对持续崩溃的作业,以及磁盘上几十上百 GB 的快照文件,自己也随之崩溃了:这么大的状态,究竟里面存了什么?能不能删点内容呢?下文笔者将带领大家分析 Flink 快照系统,找出影响大状态和数据倾斜的算子。

一、快照的类型
Flink 的快照包括 Checkpoint(周期触发)和 Savepoint(用户主动触发)两种,其中 Checkpoint 分为普通 Checkpoint 和外部化(Externalized)Checkpoint。普通 Checkpoint 只能用于本次 JobManager 存活期间的内部恢复;而外部化 Checkpoint 和 Savepoint 可以用于从零开始的冷启动恢复。
对于 Savepoint,以及开启了 外部化特性(https://ci.apache.org/projects/flink/flink-docs-master/docs/ops/state/checkpoints/#externalized-checkpoints) 的 Checkpoint,Flink 会在快照目录生成一个元数据文件(快照目录中名为 _metadata 的文件),这个文件是我们分析快照时至关重要的线索。

二、快照的存储格式
我们先从这个元数据(_metadata 文件)入手,看一下它的数据结构:

 Flink 快照 _metadata 文件结构
在 Master State 的不定长结构中,也有自己的 Magic Number、数据长度等信息,通常不会有太多数据。
Operator State 是状态的大头,在它的不定长结构中,主要包含了每个 Operator 的 ID(由两个 Long 拼起来组成),以及当前算子的并行度(parallelism)和最大并行度(maximum parallelism),还有子任务(subtask)状态的个数、每个子任务的 index、元数据(是否包含 raw 和 managed 的 Operator State、是否包含 raw 和 managed 的 Keyed State、包含哪些具体的状态、 KeyGroup 范围、偏移量、是否是 Incremental 状态、状态文件的指针 RelativeFileStateHandle 等)。
除元数据文件以外,还有很多具体的状态文件(RelativeFileStateHandle 指针指向文件),它们通常是因为尺寸过大而不能直接嵌入 _metadata 文件,只能以独立文件的方式存在的状态。

三、快照的读取方式
从上文可以看到,解析状态文件并非易事,有很多需要考虑的地方。解铃还须系铃人,我们可以用 Flink 自身来实现状态文件的读取和解析:
1. Flink 内部 API
最简单的方式,是找到 Flink 恢复快照状态的源码,然后按图索骥查找反序列化 _metadata 文件的类。很快,我们就找到了 org.apache.flink.runtime.checkpoint.Checkpoints#loadCheckpointMetadata 这个静态方法,它可以将给定的数据流反序列化成 Flink 内部的 CheckpointMetadata 对象(即上述文件的内存映射)。
如果只想处理元数据信息,而不涉及到读写具体的状态数据时,可以采用该方法。

2. 封装后的 State Processor API
在新的 Flink 版本中,还包含了封装后的 State Processor API(https://ci.apache.org/projects/flink/flink-docs-release-1.13/docs/libs/state_processor_api/),通过这个 API,我们不仅可以读取具体的状态文件,还可以按需生成状态数据以供新的 Flink 作业使用。
使用 State Processor API 时,由于涉及到具体状态的读写,需要给定 StateBackend 实例,以及具体的 Operator UID 等信息,且是以 DataSet 批处理任务方式执行的,流程相对复杂,本文不再展开描述,后续会有单独的文章介绍其使用方式。

四、一起实践
我们来尝试使用 Flink 内部 API 来读取状态元数据信息,并统计分析哪些 Operator 的状态占比最大,以及这些 Operator 的各个 Subtask(多个并行度下的子任务)的状态用量。
示例代码(读取 Flink 快照信息)非常简单,这里展示一下具体的分析结果:

可以看到,元数据文件里的各项信息都被打印输出了,而且显示出了 e0aeb8ff30fd730af442dff8ae5f50b9 这个 Operator 的状态占比最大,达到了 95.80%,且各个(总共30个) Subtask 的状态量较为平均,都在 40M~50M 之间,基本不存在数据倾斜的现象。
由于元数据里并不包含这个 Operator 的名字和类型等信息,需要通过查找日志搜索这个 Operator ID。从日志中可以看到是一个 WindowDoFnOperator 的算子。

[root@SND-VN-1869 ~]# yarn logs -applicationId application_1632363415606_9164| grep e0aeb8ff30fd730af442dff8ae5f50b9 | more
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/hadoop-2.9.0/share/hadoop/common/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/hadoop-2.9.0/share/hadoop/common/lib/slf4j-log4j12-1.7.21.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
2021-10-20 02:43:54,620 INFO  org.apache.flink.contrib.streaming.state.RocksDBKeyedStateBackendBuilder  - Finished building RocksDB keyed state-b
ackend at /hdfs_data6/tmp/nm-local-dir/usercache/root/appcache/application_1632363415606_9164/flink-io-0e4cdc01-3a7b-48f8-83e4-300d9c47210e/job_0
0000000000000000000000000000000_op_WindowDoFnOperator_e0aeb8ff30fd730af442dff8ae5f50b9__8_30__uuid_06a39857-d961-44db-b83f-da361a14a716.
2021-10-20 02:43:54,658 INFO  org.apache.flink.contrib.streaming.state.RocksDBKeyedStateBackendBuilder  - Finished building RocksDB keyed state-b
ackend at /hdfs_data5/tmp/nm-local-dir/usercache/root/appcache/application_1632363415606_9164/flink-io-317d1589-d2e8-4a18-9136-00432ea793a6/job_0
0000000000000000000000000000000_op_WindowDoFnOperator_e0aeb8ff30fd730af442dff8ae5f50b9__1_30__uuid_9a2efef9-c498-4030-bd9a-f685fc7efcd2.
2021-10-20 02:43:54,749 INFO  org.apache.flink.contrib.streaming.state.RocksDBKeyedStateBackendBuilder  - Finished building RocksDB keyed state-b
ackend at /hdfs_data7/tmp/nm-local-dir/usercache/root/appcache/application_1632363415606_9164/flink-io-aecdfe1c-2c37-4631-8894-dee3d528a5b1/job_0
0000000000000000000000000000000_op_WindowDoFnOperator_e0aeb8ff30fd730af442dff8ae5f50b9__7_30__uuid_5eafbb19-34cf-4510-a08c-a59d412f647b.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

magic_kid_2010

你的支持将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值