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

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

文章目录

在 Java / Flink 开发中,我们经常看到字段前面加了一个 transient
但很多人只知道“照着写”,并不清楚它到底解决了什么问题

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

一、transient 是干什么的?

一句话定义:

transient 用来告诉 Java:这个字段不要参与对象的序列化与反序列化。
classUserimplementsSerializable{String name;transientString password;}

序列化之后:

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

二、Java 默认的序列化规则

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

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

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

也就是说:

privateValueState<LastPoint> lastPoint;

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


三、如果不加 transient 会怎样?

1️⃣ 普通 Java 场景

ObjectOutputStream oos =newObjectOutputStream(...); oos.writeObject(obj);
  • transient 字段不会被写入
  • transient 字段会被写入

这是 Java 层面的规则


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

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

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

如果你不加 transient

privateValueState<LastPoint> lastPoint;

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

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

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


很多人以为:

ValueState 是存在这个字段里的

这是 错误的

真实结构是:

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

也就是说:

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

原因一:State 不是业务数据

State:

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

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


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

Flink 的正确流程是:

new Operator() ↓ open() ↓ getRuntimeContext().getState(...) 

而不是:

反序列化旧对象里的 state 

所以 State 字段必须:

  • transient
  • open() 中初始化

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

✅ 必须 / 强烈建议加 transient

  • ValueState
  • ListState
  • MapState
  • ReducingState
  • 运行时句柄(Metric、client、连接等)
privatetransientValueState<A> stateA;

❌ 不要加 transient

  • 配置类
  • 常量
  • 业务规则
  • 普通 POJO 字段
privatefinalTrajConfig cfg;

这些字段:

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

七、常见误区

❌ 误区 1:不加 transient 也能跑

是的,但只是在:

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

👉 这是延迟爆炸型 Bug


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

publicMyFn(){ lastPoint =getRuntimeContext().getState(...);// ❌}

原因:

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

八、推荐的标准写法模板

publicclassMyProcessFunctionextendsKeyedProcessFunction<K, IN, OUT>{privatefinalConfig cfg;privatetransientValueState<A> stateA;privatetransientListState<B> buffer;privatetransientMapState<Long,Boolean> dedup;@Overridepublicvoidopen(Configuration parameters){ stateA =getRuntimeContext().getState(newValueStateDescriptor<>("stateA",A.class)); buffer =getRuntimeContext().getListState(newListStateDescriptor<>("buffer",B.class)); dedup =getRuntimeContext().getMapState(newMapStateDescriptor<>("dedup",Long.class,Boolean.class));}}

九、总结

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

在 Flink 中可以再加一句:

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

Read more

C++ 继承入门(上):从基础概念定义到默认成员函数,吃透类复用的核心逻辑

C++ 继承入门(上):从基础概念定义到默认成员函数,吃透类复用的核心逻辑

🔥小叶-duck:个人主页 ❄️个人专栏:《Data-Structure-Learning》 《C++入门到进阶&自我学习过程记录》《算法题讲解指南》--从优选到贪心 ✨未择之路,不须回头 已择之路,纵是荆棘遍野,亦作花海遨游 目录 前言 一. 继承的概念与定义   1、继承的核心概念   2、继承的定义格式   3、继承方式与成员访问权限 二. 基类与派生类的转换:子类对象能当父类用吗? 三. 继承中的作用域:同名成员会冲突吗?   1、变量隐藏   2、函数隐藏 四、派生类的默认成员函数:构造、拷贝、析构怎么写?   1、构造函数:先调用父类构造,再初始化子类成员   2、拷贝构造:先拷贝父类,再拷贝子类   3、 赋值重载:

By Ne0inhk
2024第十五届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组

2024第十五届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组

记录刷题的过程、感悟、题解。 希望能帮到,那些与我一同前行的,来自远方的朋友😉 大纲:  1、握手问题-(解析)-简单组合问题(别人叫她 鸽巢定理)😇,感觉叫高级了  2、小球反弹-(解析)-简单物理问题,不太容易想  3、好数-(解析)-简单运用分支计算  4、R 格式-(解析)-高精度,不是快速幂😉  5、宝石组合-(解析)-lcm推论(gcd、lcm结合)  6、数字接龙-(解析)-DFS(蓝桥专属、每年必有一道)  7、拔河-(解析)-定一端,动一端😎 题目: 1、握手问题 问题描述

By Ne0inhk
【C++笔记】STL详解:string的实现

【C++笔记】STL详解:string的实现

前言:                 在前面的学习中,我们已经初步掌握了string类接口函数的使用方法,本文将带领大家从零开始,逐步实现一个完整的string类。          一、string类总览                 温馨提示: 为了避免与标准库中的string产生命名冲突,我们使用mystd命名空间进行封装。 namespace mystd { class string { public: //迭代器 typedef char* iterator; typedef const char* const_iterator; //默认成员函数 string(); string(const char* str); //构造函数 string(const string& s); //拷贝构造函数 string& operator=(const string& s); //赋值运算符重载函数 ~string(); //析构函数 //迭代器相关函数 iterator begin(

By Ne0inhk