根本原因是序列化器不匹配:jdkserializationredisserializer存的数据无法被stringredisserializer正确读取,导致反序列化失败、返回null或抛异常;需通过迁移脚本将旧数据用原序列化器取出并转为json重存。

Redis里明明有数据,但Spring Boot读不出来
根本原因不是连接失败或配置错误,而是序列化器不匹配。你用 JdkSerializationRedisSerializer(Spring Boot RedisTemplate 默认)存的数据,现在却用 StringRedisSerializer 去读——就像拿UTF-8解码器去读GBK字节流,必然失败。
典型现象包括:Cannot deserialize value of type、java.lang.ClassCastException、或者直接返回 null 却无报错。这不是“查不到”,是“解不开”。
- 旧数据是二进制格式(含类名、serialVersionUID等Java元信息),
StringRedisSerializer会尝试把整段字节当UTF-8字符串解析,结果多数字节非法,反序列化中途抛异常 -
StringRedisTemplate默认用StringRedisSerializer,但它只能读写纯字符串;若你之前混用了RedisTemplate存对象,它完全无法兼容 - 即使 key 能匹配上,value 反序列化失败也会让
opsForValue().get(key)静默返回null
统一改用 StringRedisSerializer 后,怎么读旧数据?
不能“读”,只能“迁移”。StringRedisSerializer 和 JdkSerializationRedisSerializer 是互斥的两种编码体系,没有通用解码路径。你必须主动转换存量数据。
- 用原始的
RedisTemplate(带 JDK 序列化器)先取出所有旧 key-value,手动反序列化成 Java 对象 - 再用新配置的
StringRedisTemplate或指定StringRedisSerializer的RedisTemplate,把对象转成 JSON 字符串重新写入 - 推荐脚本化处理:在启动类里加一个
@PostConstruct方法,扫描特定前缀的 key,逐条迁移,完成后删掉逻辑 - 切勿在运行时混合使用两套序列化器读写同一组 key,否则数据状态不可控
StringRedisSerializer 真的适合所有场景吗?
只适合 key 和 value 都是纯字符串的场景。一旦涉及 Java 对象(如 User、Map、LocalDateTime),StringRedisSerializer 就无能为力——它不负责类型转换,只做字节 ↔ UTF-8 字符串的直译。
- 如果你调
redisTemplate.opsForValue().set("user:1", new User()),而 value 序列化器是StringRedisSerializer,会直接抛IllegalArgumentException: Cannot serialize - 想存对象,必须换
Jackson2JsonRedisSerializer或GenericJackson2JsonRedisSerializer,并确保字段可被 Jackson 序列化(如LocalDateTime需配JavaTimeModule) -
StringRedisTemplate是开箱即用的安全选择,但它的泛型固定为<string string></string>,硬编码了序列化行为,灵活性极低
最易被忽略的细节:key 也得统一序列化
很多人只改 setValueSerializer,却忘了 setKeySerializer。Redis 的 key 本质也是字节数组,如果 key 用 JDK 序列化器生成(比如 new byte[]{1,2,3}),而你用 StringRedisSerializer 查 "user:1",压根就不是同一个 key。
- 检查 CLI 中实际存在的 key 名:执行
KEYS *,看显示的是可读字符串还是乱码(如\xac\xed\x00\x05t\x00\x07user:1) - 若 key 是乱码,说明它也被 JDK 序列化过,此时连
DEL命令都得用原始序列化方式构造 key 字节数组才能删掉 - 统一方案必须同时设置:
template.setKeySerializer(new StringRedisSerializer())和template.setValueSerializer(new StringRedisSerializer()) - 线上迁移前,务必在测试环境验证 key 扫描逻辑(
SCAN+ 模式匹配)是否能覆盖所有目标 key











