Redis频繁序列化/反序列化拖慢应用,因JSON/Java Serializable性能低、体积大、耦合强;推荐Protobuf替代,需显式配置序列化器、禁用自动泛型解析,并规避DynamicMessage和Any类型等坑点。

为什么 Redis 里频繁 serialize/deserialize 会拖慢应用
Redis 本身只存字节流,所有对象进出都得靠应用侧编解码。如果用 JSON 或默认 Java Serializable,每次写入都要走反射+字符串拼接,读取还要解析树结构+类型推断——尤其在高吞吐场景下,CPU 花在编解码上的时间可能超过网络和 Redis 本身的耗时。
更隐蔽的问题是:JSON 字符串体积大,增加网络传输和 Redis 内存占用;Java Serializable 有类版本强耦合,升级字段稍不注意就 InvalidClassException。
- 高频小对象(如用户 session、订单状态)建议直接上二进制协议
- 只要业务能接受“序列化格式不 human-readable”,Protobuf 就比 JSON 快 3–5 倍,体积小 50%–70%
- 别在 Redis 客户端层做自动泛型反序列化(比如 Spring Data Redis 的
GenericJackson2JsonRedisSerializer),它会在每次 get 时 new ObjectMapper 实例或反复调用反射
用 Protobuf 替换 JSON 时必须改的三处代码
不是加个依赖就能生效,关键路径要显式控制序列化入口。以 Java + Lettuce 为例:
-
实体类必须生成
.proto文件并编译出Message子类:不能直接拿 POJO 去塞 Protobuf,否则运行时报ClassCastException -
RedisTemplate 的
valueSerializer改为自定义ProtobufSerializer:继承RedisSerializer<T>,重写serialize()调message.toByteArray(),deserialize()用MyProtoClass.parseFrom(byte[]) -
禁用 Spring 的自动类型推断:去掉
@EnableCaching下的GenericJackson2JsonRedisSerializer配置,否则opsForValue().get(key)可能悄悄 fallback 到 JSON 解析
示例片段:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
public class ProtobufSerializer<T extends Message> implements RedisSerializer<T> {
private final Class<T> targetClass;
public ProtobufSerializer(Class<T> targetClass) { this.targetClass = targetClass; }
@Override
public byte[] serialize(T object) { return object.toByteArray(); }
@Override
public T deserialize(byte[] bytes) {
try { return targetClass.getDeclaredMethod("parseFrom", byte[].class)
.invoke(null, bytes);
} catch (Exception e) { throw new RuntimeException(e); }
}
}
Protobuf 在 Redis 场景下的两个典型踩坑点
不是所有 Protobuf 使用方式都适合缓存场景:
-
不要用
DynamicMessage或反射解析:虽然灵活,但每次 parse 都要加载 descriptor、校验字段,性能接近 JSON;固定 schema 才能享受 Protobuf 的零拷贝优势 -
嵌套对象别用
Any类型存整个子对象:Any.pack()会把子消息再 encode 一次,造成双重序列化;应拆成独立字段,或用oneof显式声明可选类型 - 如果用
StringRedisTemplate存 Protobuf,记得手动new String(bytes, StandardCharsets.ISO_8859_1)—— 直接 toString() 会乱码,且下次 deserialize 失败
什么时候该放弃 Protobuf,退回 JSON 或干脆存字符串
Protobuf 不是银弹。以下情况反而增加复杂度:
- 缓存内容需被非 Java 系统(如 Node.js 管理后台)直接读取,又不愿引入 Protobuf JS 库时,JSON 更实际
- 数据结构极不稳定(比如每天加字段、删字段),Protobuf 的向后兼容要求(必须设
optional、保留字段号)会拖慢迭代节奏 - 只是存 token、短链接、开关标识这类纯字符串,直接
set key "true",连序列化都省了
真正省资源的地方,是那些高频读写、结构稳定、跨服务共享的领域模型——比如 OrderStatus、UserProfile。其他地方硬套 Protobuf,容易让团队卡在编译 .proto 和维护 build.gradle 插件上。

















