一文讲透 Java 中transient的用处(结合 Flink 理解)

文章目录

  • [一、transient 是干什么的?](#一、transient 是干什么的?)
  • [二、Java 默认的序列化规则](#二、Java 默认的序列化规则)
  • [三、如果不加 transient 会怎样?](#三、如果不加 transient 会怎样?)
  • [四、Flink 中 State 的真实存储位置](#四、Flink 中 State 的真实存储位置)
  • [五、为什么 Flink State 一定要 transient?](#五、为什么 Flink State 一定要 transient?)
  • [六、哪些字段该加 transient?哪些不该?](#六、哪些字段该加 transient?哪些不该?)
  • 七、常见误区
  • 八、推荐的标准写法模板
  • 九、总结

在 Java / Flink 开发中,我们经常看到字段前面加了一个 transient

但很多人只知道"照着写",并不清楚它到底解决了什么问题

本文从 Java 序列化原理 出发,再结合 Flink State 的真实运行模型 ,一次性把 transient 讲清楚。


一、transient 是干什么的?

一句话定义:

transient 用来告诉 Java:这个字段不要参与对象的序列化与反序列化。

java 复制代码
class User implements Serializable {
    String name;
    transient String password;
}

序列化之后:

  • name 会被保存
  • password 会被忽略,反序列化后为 null

二、Java 默认的序列化规则

在 Java 中,只要满足以下条件:

  • 类实现了 Serializable
  • 字段 不是 static
  • 字段 不是 transient

👉 那么这个字段就会被 自动序列化

也就是说:

java 复制代码
private ValueState<LastPoint> lastPoint;

在 Java 看来,这只是一个普通成员变量,会跟着对象一起被序列化。


三、如果不加 transient 会怎样?

1️⃣ 普通 Java 场景

java 复制代码
ObjectOutputStream oos = new ObjectOutputStream(...);
oos.writeObject(obj);
  • transient 字段不会被写入
  • transient 字段会被写入

这是 Java 层面的规则


2️⃣ 在 Flink 中的隐蔽问题(重点)

Flink 在以下场景中,会涉及算子对象的序列化:

  • 作业下发(JobManager → TaskManager)
  • Task 重启 / failover
  • checkpoint / savepoint
  • 扩缩容(rescale)

如果你不加 transient

java 复制代码
private ValueState<LastPoint> lastPoint;

Java 会尝试序列化这个 State 句柄对象,而这通常会导致:

  • NotSerializableException
  • 作业启动失败
  • checkpoint 异常
  • failover 后状态错乱

📌 很多问题不是立刻出现,而是线上才爆


很多人以为:

ValueState 是存在这个字段里的

这是 错误的

真实结构是:

text 复制代码
Operator 对象(Java)
 └── transient ValueState handle(句柄)
        ↓
 StateBackend(RocksDB / 内存)
 └── 真正的状态数据

也就是说:

  • ValueState / ListState / MapState
    👉 只是一个"访问入口(handle)"
  • 真正的数据由 Flink StateBackend 管理

原因一:State 不是业务数据

State:

  • 不是你对象的一部分
  • 不该由 Java 序列化
  • 不该跟着对象"复制"

它的生命周期由 Flink 运行时 管理,而不是 JVM。


原因二:State 需要在运行时重新绑定

Flink 的正确流程是:

text 复制代码
new Operator()
  ↓
open()
  ↓
getRuntimeContext().getState(...)

而不是:

text 复制代码
反序列化旧对象里的 state

所以 State 字段必须:

  • transient
  • open() 中初始化

六、哪些字段该加 transient?哪些不该?

✅ 必须 / 强烈建议加 transient

  • ValueState
  • ListState
  • MapState
  • ReducingState
  • 运行时句柄(Metric、client、连接等)
java 复制代码
private transient ValueState<A> stateA;

❌ 不要加 transient

  • 配置类
  • 常量
  • 业务规则
  • 普通 POJO 字段
java 复制代码
private final TrajConfig cfg;

这些字段:

  • 需要被序列化
  • 需要在每个 task 中保持一致

七、常见误区

❌ 误区 1:不加 transient 也能跑

是的,但只是在:

  • 本地模式
  • 未开启 checkpoint
  • 未发生 failover

👉 这是延迟爆炸型 Bug


❌ 误区 2:在构造函数里初始化 State

java 复制代码
public MyFn() {
    lastPoint = getRuntimeContext().getState(...); // ❌
}

原因:

  • 构造阶段还没有 RuntimeContext
  • state 绑定信息还不存在

八、推荐的标准写法模板

java 复制代码
public class MyProcessFunction extends KeyedProcessFunction<K, IN, OUT> {

    private final Config cfg;

    private transient ValueState<A> stateA;
    private transient ListState<B> buffer;
    private transient MapState<Long, Boolean> dedup;

    @Override
    public void open(Configuration parameters) {
        stateA = getRuntimeContext().getState(
                new ValueStateDescriptor<>("stateA", A.class));
        buffer = getRuntimeContext().getListState(
                new ListStateDescriptor<>("buffer", B.class));
        dedup = getRuntimeContext().getMapState(
                new MapStateDescriptor<>("dedup", Long.class, Boolean.class));
    }
}

九、总结

transient 的本质作用:
阻止 Java 默认序列化,
让运行时资源交给运行时系统(如 Flink)管理。

在 Flink 中可以再加一句:

State 的生命周期属于 Flink,不属于 Java 对象。

相关推荐
Coder_Boy_1 小时前
基于SpringAI的在线考试系统-教学管理与用户管理模块联合回归测试文档
java·前端·数据库·人工智能·spring boot
独行soc1 小时前
2026年渗透测试面试题总结-5(题目+回答)
android·网络·python·安全·web安全·渗透测试
玩大数据的龙威2 小时前
农经权二轮延包—一键出承包地块调查表
数据库·python
越甲八千2 小时前
python socket
开发语言·python
xqqxqxxq2 小时前
《智能仿真无人机平台(多线程V1.0)技术笔记》(初识线程,带你理解程序运行的基本流程)
java·笔记
进阶小白猿2 小时前
Java技术八股学习Day23
java·网络·学习
砚边数影2 小时前
DL4J框架入门(三):基础配置,计算后端(CPU/GPU)选型与优化
java·数据库·人工智能·ai·金仓数据库
名字无法显示3412 小时前
Arthas 实战指南:结合 IDEA 的 Java 线上排查完整流程
java·intellij-idea
爱吃肉的鹏2 小时前
树莓派4B安装pytorch
人工智能·pytorch·python