Spring Data Redis 实战避坑:搞定序列化乱码与 Hash 结构存储 Spring Data Redis 实战避坑搞定序列化乱码与 Hash 结构存储在 Java 后端开发中Redis 几乎是高并发场景下的标配。虽然 Redis 自带的命令行客户端redis-cli功能强大但在实际项目中我们更多是通过 Java 客户端来与 Redis 进行交互。下面将讲述最容易踩到的“序列化乱码”深坑以及如何优雅地使用 Hash 结构存储对象。一、 从 Jedis 到 Spring Data Redis在 Java 生态中操作 Redis 的主流客户端有 Jedis 和 Lettuce。早期的 Jedis 实例是线程不安全的为了避免频繁创建和销毁 TCP 连接带来的性能损耗我们通常需要引入 Jedis 连接池JedisPool。而 Spring Data Redis 作为 Spring 家族的一员对上述客户端进行了高度封装。它提供了统一的 API ——RedisTemplate不仅屏蔽了底层客户端的差异还支持 Redis 哨兵、集群以及响应式编程。在 Spring Boot 项目中我们只需要引入spring-boot-starter-data-redis和连接池依赖commons-pool2就可以在 YAML 中配置好 Redis 的连接信息通过Autowired直接注入RedisTemplate来使用。二、 避坑指南序列化导致的“乱码”危机RedisTemplate虽然好用但很多新手在第一次使用时都会遇到一个诡异的现象明明在 Java 代码中设置了一个 Key但在 Redis 图形化客户端中查看时Key 前面却多了一长串看不懂的乱码或者存入的 Value 变成了一串二进制字节。这是因为RedisTemplate默认采用了 JDK 序列化JdkSerializationRedisSerializer。这种序列化方式虽然能保留对象的完整结构但可读性极差且占用的内存空间较大。为了解决这个问题我们需要自定义RedisTemplate的序列化策略。通常的最佳实践是Key 采用 String 序列化Value 采用 JSON 序列化。我们可以通过以下配置类将序列化方式替换为Jackson2JsonRedisSerializerConfigurationpublicclassRedisConfig{BeanSuppressWarnings({rawtypes,unchecked})publicRedisTemplateString,ObjectredisTemplate(RedisConnectionFactoryfactory){RedisTemplateString,ObjecttemplatenewRedisTemplate();template.setConnectionFactory(factory);// 1. 配置 Value 的 JSON 序列化Jackson2JsonRedisSerializerObjectjackson2JsonRedisSerializernewJackson2JsonRedisSerializer(Object.class);ObjectMapperomnewObjectMapper();om.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);om.activateDefaultTyping(om.getPolymorphicTypeValidator(),ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// 2. 配置 Key 的 String 序列化StringRedisSerializerstringRedisSerializernewStringRedisSerializer();// 3. 注入序列化器template.setKeySerializer(stringRedisSerializer);template.setHashKeySerializer(stringRedisSerializer);template.setValueSerializer(jackson2JsonRedisSerializer);template.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();returntemplate;}}经过这番配置你再存入数据时Key 就是清清爽爽的字符串而 Value 也会变成可读性极强的 JSON 格式。三、 另一种选择StringRedisTemplate如果你觉得自定义序列化配置太麻烦或者你的业务场景中缓存的大多是简单的字符串比如 Token、验证码Spring Boot 其实还提供了一个开箱即用的StringRedisTemplate。它的 Key 和 Value 默认都采用 String 序列化。如果你需要存储 Java 对象只需要在写入前手动将对象序列化为 JSON 字符串读取时再手动反序列化即可个人在学习中常用此方法AutowiredprivateStringRedisTemplatestringRedisTemplate;AutowiredprivateObjectMapperobjectMapper;// 存入对象publicvoidsaveUser(Useruser)throwsException{StringjsonobjectMapper.writeValueAsString(user);stringRedisTemplate.opsForValue().set(user:user.getId(),json,30,TimeUnit.MINUTES);}// 取出对象publicUsergetUser(Stringid)throwsException{StringjsonstringRedisTemplate.opsForValue().get(user:id);returnjson!null?objectMapper.readValue(json,User.class):null;}四、 Redis Hash 与 Java 代码的映射实战除了基础的 String 类型Redis 的 Hash 结构非常适合用来存储对象比如用户信息、商品信息。它允许我们将一个对象的多个字段拆分开来存储而不是像 String 那样必须把整个对象序列化成一个大 JSON。在 Redis 命令行中我们使用HSET和HGET来操作 Hash。而在 Java 的RedisTemplate中这些操作被封装在了HashOperations接口里。以下是 Redis 命令与 Java API 的对照实战操作场景Redis 命令Java (HashOperations)添加/更新字段HSET key field valueput(key, hashKey, value)获取单个字段HGET key fieldget(key, hashKey)获取所有字段HGETALL keyentries(key)删除字段HDEL key fielddelete(key, hashKey)代码示例AutowiredprivateRedisTemplateString,ObjectredisTemplate;publicvoidtestHash(){// 1. 获取 Hash 操作对象HashOperationsString,Object,ObjectopsForHashredisTemplate.opsForHash();// 2. 存入用户信息 (相当于 HSET user:1001 name Tom)opsForHash.put(user:1001,name,Tom);opsForHash.put(user:1001,age,25);// 3. 获取单个字段 (相当于 HGET user:1001 name)ObjectnameopsForHash.get(user:1001,name);System.out.println(用户名: name);// 4. 获取所有字段 (相当于 HGETALL user:1001)MapObject,ObjectentriesopsForHash.entries(user:1001);System.out.println(用户所有信息: entries);}五、 总结如果你需要存储复杂的对象且希望 Redis 中数据可读推荐使用自定义序列化器的RedisTemplate。如果你只是存储简单的字符串StringRedisTemplate配合手动序列化是更轻量级的选择。在操作 Hash 结构时记住HSET对应putHGETALL对应entries就能轻松搞定对象字段的增删改查。