RedisTemplate默认使用JdkSerializationRedisSerializer导致乱码、跨语言不互通及NotSerializableException;必须替换为Jackson2JsonRedisSerializer并显式指定类型反序列化,否则取值为LinkedHashMap。

RedisTemplate 默认用 JDK 序列化,存进去全是乱码,直接上线会出问题。必须替换为 JSON 序列化,否则连调试都困难。
为什么不能直接用默认的 RedisTemplate
Spring Boot 自动配置的 RedisTemplate 使用 JdkSerializationRedisSerializer,序列化后 key 和 value 都是二进制字节流,Redis CLI 里看到的是类似 \xac\xed\x00\x05t\x00\x03foo 的内容,根本没法人工排查、无法被其他语言服务消费、也不支持跨版本反序列化。
- JSON 序列化能让数据可读、可调试、可协作(比如前端或 Python 服务也能读)
- 必须同时设置
keySerializer(用StringRedisSerializer)和valueSerializer(用GenericJackson2JsonRedisSerializer或自定义Jackson2JsonRedisSerializer) - 如果业务含 Java 8 时间类型(
LocalDateTime等),需额外注册Jackson2ObjectMapperBuilder并引入jackson-datatype-jsr310依赖
@Cacheable 注解缓存 null 值会导致穿透风险
Spring Cache 默认允许缓存 null 返回值,一旦数据库查不到数据,@Cacheable 就把 null 写进 Redis —— 下次请求照样击中缓存,永远绕不开 DB,形成缓存穿透。
- 在
CacheManager配置里显式调用.disableCachingNullValues() - 搭配布隆过滤器(Bloom Filter)或空对象(如
"empty"字符串)做兜底,但注意避免误判和内存膨胀 - 不要依赖
@Cacheable(unless = "#result == null"),它只跳过写入,不阻止首次缓存 null(取决于执行顺序)
Lettuce 连接池参数配错会拖垮整个应用
默认的 Lettuce 连接池(max-active: 8、max-wait: -1ms)在高并发下极易卡死:-1 表示无限等待,线程全堵在 getConnection() 上,HTTP 请求超时、线程耗尽、雪崩一气呵成。
-
max-wait必须设为有限值(如100ms),超时抛异常比无限挂起更可控 -
min-idle设为非 0(如2),避免冷启动时连接建立延迟 -
max-active不宜盲目调大(如 >32),Lettuce 是基于 Netty 的异步客户端,连接数过多反而增加调度开销 - 线上建议配合
lettuce: shutdown-timeout: 100ms防止应用停机时连接泄漏
缓存失效策略选错,一致性很难兜住
用 @CacheEvict 删除缓存看似简单,但在分布式环境下,多个实例同时更新 DB + 同时删缓存,大概率出现“先删缓存、再改 DB”期间的脏读;或者“改 DB 成功、删缓存失败”导致永久不一致。
- 优先用「先更新 DB,再删除缓存」+ 最终一致性补偿(如监听 binlog 或发 MQ 消息异步删)
- 避免用「更新缓存」代替「删除缓存」,除非你能保证所有写路径都覆盖且无并发冲突
- 对强一致性要求高的场景(如账户余额),别走缓存,直接查 DB + 加行锁或乐观锁


















