您的位置:首页 > 数据库 > Redis

使用Spring Cache + Redis + Jackson Serializer缓存数据库查询结果中序列化问题的处理

2019-04-11 14:50 2401 查看

文章目录

  • demo测试地址
  • 参考
  • 应用场景

    我们希望通过缓存来减少对关系型数据库的查询次数,减轻数据库压力。在执行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
    来配置序列化。

    @Bean
    public RedisCacheManager jsonCacheManager(RedisConnectionFactory factory) {
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
    .serializeValuesWith(RedisSerializationContext.SerializationPair
    .fromSerializer(new GenericJackson2JsonRedisSerializer()));
    return RedisCacheManager.builder(factory)
    .cacheDefaults(config)
    .build();
    }

    Jackson2JsonRedisSerializer和GenericJackson2JsonRedisSerializer的区别

    一、使用Jackson2JsonRedisSerializer序列化反序列化带泛型的List数据

    1、使用Jackson2JsonRedisSerializer序列化value的代码

    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
    User user = new User();
    user.setUserId(1);
    user.setUsername("张三");
    List<User> userList = new ArrayList<>();
    userList.add(user);
    //不能直接将对象存储进redis中否则在进行反序列化的时候会报
    // java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.lx.entity.User错误
    //可以先转为json字符串再进行存储
    String value = JSON.toJSONString(userList);
    redisTemplate.opsForValue().set("jackson2JsonRedisSerializer", value, 5,
    TimeUnit.MINUTES);

    2、使用Jackson2JsonRedisSerializer序列化后的数据形式

    [
    {
    "userId":1,
    "username":"张三"
    }
    ]

    3、使用Jackson2JsonRedisSerializer反序列化时报错

    List<User> userListRedis = redisTemplate.opsForValue().get("jackson2JsonRedisSerializer");
    userListRedis.forEach(u -> System.out.println(JSON.toJSONString(u)));

    错误信息

    java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.lx.entity.User
    • 原因: 序列化带泛型的数据时,会以map的结构进行存储,反序列化时不能将map解析成对象。

    4、解决方案:序列化存储时,转成JSON字符串
    使用

    jackson
    或者
    fastjson
    都可以,我这里使用的是
    fastjson
    ,
    需要的依赖

    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
    </dependency>
    Us
    20000
    er user = new User();
    user.setUserId(1);
    user.setUsername("张三");
    List<User> userList = new ArrayList<>();
    userList.add(user);
    
    redisTemplate.opsForValue().set("jackson2JsonRedisSerializer", JSON.toJSONString(userList), 5, TimeUnit.MINUTES);
    
    String res = (String) redisTemplate.opsForValue().get("jackson2JsonRedisSerializer");
    JSON.parseArray(res, User.class).forEach(u -> System.out.println(JSON.toJSONString(u)));

    二、使用GenericJackson2JsonRedisSerializer序列化反序列化带泛型的List数据

    1、使用GenericJackson2JsonRedisSerializer序列化value的代码

    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    User user = new User();
    user.setUserId(1);
    user.setUsername("李四");
    List<User> userList = new ArrayList<>();
    userList.add(user);
    redisTemplate.opsForValue().set("genericJackson2JsonRedisSerializer",userList,5,TimeUnit.MINUTES);

    2、使用GenericJackson2JsonRedisSerializer序列化后的数据形式

    [
    "java.util.ArrayList",
    [
    {
    "@class": "com.lx.entity.User",
    "userId": 1,
    "username": "李四"
    }
    ]
    ]

    3、使用GenericJackson2JsonRedisSerializer可以正常反序列化

    List<User> userListRedis = (List<User>) redisTemplate.opsForValue().get("genericJackson2JsonRedisSerializer");
    userListRedis.forEach(u -> System.out.println(JSON.toJSONString(u)));

    4、可以正常反序列化的原因
    使用

    GenericJackson2JsonRedisSerializer
    序列化时,会保存序列化的对象的包名和类名,反序列化时以这个作为标示就可以反序列化成指定的对象。

    5、也可以以JSON字符串保存

    redisTemplate.opsForValue().set("genericJackson2JsonRedisSerializer",JSON.toJSONString(userList),5,TimeUnit.MINUTES);
    
    List<User> userListRedis = (List<User>) redisTemplate.opsForValue().get("genericJackson2JsonRedisSerializer");
    userListRedis.forEach(u -> System.out.println(JSON.toJSONString(u)));

    四、GenericJackson2JsonRedisSerializer和Jackson2JsonRedisSerializerdo效率

    User user = new User();
    user.setUserId(1);
    user.setUsername("李四");
    List<User> userList = new ArrayList<>();
    for (int i = 0; i < 10000; i++) {
    userList.add(user);
    }
    redisTemplate.opsForValue().set("serializer", JSON.toJSONString(userList),5,TimeUnit.MINUTES);
    long end = System.currentTimeMillis();
    System.out.println("Serializer序列化需要的时间:" + (end - start));

    测试后:

    jackson2JsonRedisSerializer序列化需要的时间:687
    genericJackson2JsonRedisSerializer序列化需要的时间:22

    总结

    • 使用

      Jackson2JsonRedisSerializer
      需要指明序列化的类Class,可以使用
      Obejct.class

    • 使用

      GenericJackson2JsonRedisSerializer
      Jackson2JsonRedisSerializerdo
      都可以正常序列化非泛型数组对象。
      GenericJackson2JsonRedisSerializer
      也可以正常反序列化非泛型数组对象,但是
      Jackson2JsonRedisSerializerdo
      因为“序列化带泛型的数据时,会以map的结构进行存储,反序列化时不能将map解析成对象”,所以不能反序列化,解决办法: 存储以JSON字符串存储

    • 使用

      GenericJacksonRedisSerializer
      Jackson2JsonRedisSerializer
      效率高

    • GenericJacksonRedisSerializer
      Jackson2JsonRedisSerializer
      都是以JSON格式去存储数据,都可以作为Redis的序列化方式

    demo测试地址

    https://gitee.com/fengzxia/spring-boot-redis-cache/blob/master/src/test/java/com/lx/TestSerializer.java

    参考

    https://blog.csdn.net/neosmith/article/details/46800235
    https://blog.csdn.net/bai_bug/article/details/81222519

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: 
    相关文章推荐