一、引出问题
在上节我们已经学习了自定义 RedisSerializer
来实现全自动JSON的序列化和反序列化,这样依赖当我们向Redis中写入一个Java对象的时候就方便很多。
尽管JSON的序列化方式可以满足我们的需求,但依然存在一些问题,如图:
为了在反序列化时知道对象的类型,JSON序列化器会将类的class类型写入json结果中,并且这个字节码本身甚至比我数据类型还要长,存入Redis就会带来额外的内存开销。
但是也不能不写这个东西,序列化还好说,直接将对象转为字符串写就行了,但是反序列化的时候它怎么知道你这个JSON字符串要转成哪个类型的对象呢?因此这个东西是实现自动序列化必不可少的。
二、解决问题
因此如果你想减少内存的消耗,就不能使用JSON的序列化器来自动处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。
如下图,有一个 User类
的对象需要往redis中存,此时可以手动序列化,我们手动在序列化的时候,就不需要将字节码写进去了,直接写类本身的属性就行了,然后再调用 set()方法
写入redis。
当我们要读取数据的时候,读出来的就是一个JSON字符串,它里面没有字节码,没有字节码就没有人帮你自动处理,此时就需要手动来处理,因为程序员自己知道我取出来的是哪个类型,此时就可以转为对应的类型了。
相当于是代码复杂了,加了两个手动处理的动作,但是就解决了刚刚内存占用的问题。
不过还有一点要注意的就是:既然说了要统一使用string的序列化器,也就是说之前的RedisTemplate需要统一的定义成用StringSerializer才行,因此需要重新定义RedisTemplate。
这里告诉大家一个好消息,不需要你重新定义,因为SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。
省去了我们自定义RedisTemplate的序列化方式的步骤,而是直接使用StringRedisTemplate。
三、代码实现
@SpringBootTest
class RedisStringTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
// 存字符串
@Test
void testString() {
// 写入一条String数据
stringRedisTemplate.opsForValue().set("name", "虎哥");
// 获取string数据
Object name = stringRedisTemplate.opsForValue().get("name");
System.out.println("name = " + name);
}
// ObjectMapper是SpringMVC中默认使用的JSON处理工具,因此这里就用它了,跟以前接触的fastjson是一样的,它的作用就是将对象转成JSON,这里当然也可以使用fastjson
private static final ObjectMapper mapper = new ObjectMapper();
// 存对象
@Test
void testSaveUser() throws JsonProcessingException {
// 创建对象
User user = new User("虎哥", 21);
// 手动序列化,这里可能会有异常,碰见异常直接往外面抛即可
String json = mapper.writeValueAsString(user);
// 写入数据
stringRedisTemplate.opsForValue().set("user:200", json);
// 获取数据的时候默认就是字符串
String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
// 手动反序列化
User user1 = mapper.readValue(jsonUser, User.class);
System.out.println("user1 = " + user1);
}
}
此时我们再来看一看存储的数据,小伙伴们就会发现那个class数据已经不在了,节约了我们的空间~
四、总结
RedisTemplate的两种序列化实践方案:
-
方案一:
- 自定义RedisTemplate
- 修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer
- 弊端:会占用额外空间,用来记录class字段
-
方案二:
- 使用StringRedisTemplate
- 写入Redis时,手动把对象序列化为JSON
- 读取Redis时,手动把读取到的JSON反序列化为对象