使用Spring Cache + Redis + Jackson Serializer缓存数据库查询结果中序列化问题的处理
应用场景
我们希望通过缓存来减少对关系型数据库的查询次数,减轻数据库压力。在执行DAO类的select***(), query***()方法时,先从Redis中查询有没有缓存数据,如果有则直接从Redis拿到结果,如果没有再向数据库发起查询请求取数据。
序列化问题
要把对象做为key-value对保存在redis中,就必须要解决对象的序列化问题。Spring Data Redis给我们提供了一些现成的方案:
JdkSerializationRedisSerializer. 使用JDK提供的序列化功能。 优点是反序列化时不需要提供类型信息(class),但缺点是序列化后的结果非常庞大,是JSON格式的5倍左右,这样就会消耗redis服务器的大量内存。Jackson2JsonRedisSerializer. 使用Jackson库将对象序列化为JSON字符串。优点是速度快,序列化后的字符串短小精悍。但缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。 通过查看源代码,发现其只在反序列化过程中用到了类型信息。GenericJackson2JsonRedisSerializer. 和Jackson2JsonRedisSerializer类似。但是它不需要提供序列化对象的类型信息。
分析
如果用方案一,就必须付出缓存多占用4倍内存的代价,实在承受不起。
如果用方案二,则必须给每一种domain对象都配置一个Serializer,即如果我的应用里有100种domain对象,那就必须在spring配置文件中配置100个Jackson2JsonRedisSerializer,这显然也是不现实的。
如果用方案三,就是为了解决Jackson必须提供类型信息的问题,可以同时支持多种不同类型的domain对象。
总结
所以选择使用GenericJackson2JsonRedisSerializer来配置序列化。
1 |
|
Jackson2JsonRedisSerializer和GenericJackson2JsonRedisSerializer的区别
一、使用Jackson2JsonRedisSerializer序列化反序列化带泛型的List数据
1、使用Jackson2JsonRedisSerializer序列化value的代码
1 | redisTemplate.setKeySerializer(new StringRedisSerializer()); |
1 | User user = new User(); |
2、使用Jackson2JsonRedisSerializer序列化后的数据形式
1 | [ |
3、使用Jackson2JsonRedisSerializer反序列化时报错
1 | List<User> userListRedis = redisTemplate.opsForValue().get("jackson2JsonRedisSerializer"); |
错误信息
1 | java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.lx.entity.User |
- 原因: 序列化带泛型的数据时,会以map的结构进行存储,反序列化时不能将map解析成对象。
4、解决方案:序列化存储时,转成JSON字符串
使用jackson或者fastjson都可以,我这里使用的是fastjson,
需要的依赖
1 | <dependency> |
1 | User user = new User(); |
二、使用GenericJackson2JsonRedisSerializer序列化反序列化带泛型的List数据
1、使用GenericJackson2JsonRedisSerializer序列化value的代码
1 | redisTemplate.setKeySerializer(new StringRedisSerializer()); |
1 | User user = new User(); |
2、使用GenericJackson2JsonRedisSerializer序列化后的数据形式
1 | [ |
3、使用GenericJackson2JsonRedisSerializer可以正常反序列化
1 | List<User> userListRedis = (List<User>) redisTemplate.opsForValue().get("genericJackson2JsonRedisSerializer"); |
4、可以正常反序列化的原因
使用GenericJackson2JsonRedisSerializer序列化时,会保存序列化的对象的包名和类名,反序列化时以这个作为标示就可以反序列化成指定的对象。
5、也可以以JSON字符串保存
1 | redisTemplate.opsForValue().set("genericJackson2JsonRedisSerializer",JSON.toJSONString(userList),5,TimeUnit.MINUTES); |
四、GenericJackson2JsonRedisSerializer和Jackson2JsonRedisSerializerdo效率
1 | User user = new User(); |
测试后:
1 | jackson2JsonRedisSerializer序列化需要的时间:687 |
总结
使用
Jackson2JsonRedisSerializer需要指明序列化的类Class,可以使用Obejct.class使用
GenericJackson2JsonRedisSerializer和Jackson2JsonRedisSerializerdo都可以正常序列化非泛型数组对象。GenericJackson2JsonRedisSerializer也可以正常反序列化非泛型数组对象,但是Jackson2JsonRedisSerializerdo因为“序列化带泛型的数据时,会以map的结构进行存储,反序列化时不能将map解析成对象”,所以不能反序列化,解决办法: 存储以JSON字符串存储使用
GenericJacksonRedisSerializer比Jackson2JsonRedisSerializer效率高GenericJacksonRedisSerializer和Jackson2JsonRedisSerializer都是以JSON格式去存储数据,都可以作为Redis的序列化方式
demo测试地址
参考
https://blog.csdn.net/neosmith/article/details/46800235
https://blog.csdn.net/bai_bug/article/details/81222519
作者: 只是学习学习
邮箱: fengzxia1000@163.com
原文地址: https://fengzxia.gitee.io/posts/f6f65c4b.html
版权声明: 商业转载请联系作者获得授权,非商业转载请注明出处。