Springboot中redis序列化问题分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012031408/article/details/89478241

研究原因:

springboot中使用redis获取数据后,转换成对象时,
总是提示:“com.alibaba.fastjson.JSONException: syntax error, expect {, actual [, pos 0”,
最后发现是因为redis的序列化方式引起的。所以就对redis序列化的方式做一下分析:

1、springboot中redis存储数据序列化方式,常用的有以下几种:

  • StringRedisSerializer:将数据序列化成字符串
  • Jackson2JsonRedisSerializer:将数据序列化成json
  • GenericJackson2JsonRedisSerializer:将数据序列化成json
  • JdkSerializationRedisSerializer:将数据序列化对象

2、粗略研究一下各个序列化方式的优缺点:

  • 对于StringRedisSerializer,是最基础的,如果所有的数据都是字符串,则优先选用该种序列化方式。springboot提供的StringRedisTemplate默认的就是使用该序列化方式。注意,该种序列化方式只能保存字符串,不能保存对象
  • Jackson2JsonRedisSerializer,能保存对象,将对象序列化为json,但是在初始化的时候需要指定要序列化的类,可以使用Object.class。
  • GenericJackson2JsonRedisSerializer,与Jackson2JsonRedisSerializer功能相同,只是存在性能方面的差别,以及GenericJackson2JsonRedisSerializer在初始化时不用指定要泛化的类
  • JdkSerializationRedisSerializer,将数据序列化成对象,但是要保存的数据对象必须要实现Serializable,否则会提示没有序列化。该种序列化方式再redis中是以二进制的方式存储,所以可读性非常差。springboot中提供的RedisTemplate就默认使用该种序列化方式。如果想有好的可读性,就需要更改它的序列化方式

3、各序列化方式的性能优劣:

网上很多人说优先推荐GenericJackson2JsonRedisSerializer,是因为Jackson2JsonRedisSerializer需要指定要泛化的类,下面对Jackson2JsonRedisSerializer,GenericJackson2JsonRedisSerializer,JdkSerializationRedisSerializer三个序列化方式的序列化和反序列化能力做了一个测试:

@Slf4j
public class main {

    public static void main(String[] args) {
        JdkSerializationRedisSerializer jdkSeri = new JdkSerializationRedisSerializer();
        GenericJackson2JsonRedisSerializer genJsonSeri = new GenericJackson2JsonRedisSerializer();
        Jackson2JsonRedisSerializer jack2Seri = new Jackson2JsonRedisSerializer(Object.class);

        User user = new User("1","tom",20,"boy");
        List<Object> list = new ArrayList<>();
        for(int i = 0; i < 1000; i++){
            list.add(user);
        }

        log.info("jdkSeri序列化开始");
        long jdkSeri_Start = System.currentTimeMillis();
        byte[] serialize = jdkSeri.serialize(list);
        log.info("jdkSeri序列化结束,耗时:{}ms,序列化之后的长度为:{}==============",(System.currentTimeMillis()-jdkSeri_Start),serialize.length);
        log.info("jdkSeri开始反序列化");
        long jdkSeri_Start_again = System.currentTimeMillis();
        jdkSeri.deserialize(serialize);
        log.info("jdkSeri反序列化耗时:{}ms===========",(System.currentTimeMillis()-jdkSeri_Start_again));


        log.info("genJsonSeri序列化开始");
        long genJsonSeri_Start = System.currentTimeMillis();
        byte[] serialize1 = genJsonSeri.serialize(list);
        log.info("genJsonSeri序列化结束,耗时:{}ms,序列化之后的长度为:{}==============",(System.currentTimeMillis()-genJsonSeri_Start),serialize1.length);
        log.info("genJsonSeri开始反序列化");
        long genJsonSeri_Start_again = System.currentTimeMillis();
        genJsonSeri.deserialize(serialize1);
        log.info("genJsonSeri反序列化耗时:{}ms===========",(System.currentTimeMillis()-genJsonSeri_Start_again));


        log.info("jack2Seri序列化开始");
        long jack2Seri_Start = System.currentTimeMillis();
        byte[] serialize2 = jack2Seri.serialize(list);
        log.info("jack2Seri序列化结束,耗时:{}ms,序列化之后的长度为:{}===============",(System.currentTimeMillis()-jack2Seri_Start),serialize2.length);
        log.info("jack2Seri开始反序列化");
        long jack2Seri_Start_again = System.currentTimeMillis();
        jack2Seri.deserialize(serialize2);
        log.info("jack2Seri反序列化耗时:{}ms=============",(System.currentTimeMillis()-jack2Seri_Start_again));
    }
}
16:29:32.710 [main] INFO com.whj.springboot.config.main - jdkSeri序列化开始
16:29:32.790 [main] INFO com.whj.springboot.config.main - jdkSeri序列化结束,耗时:68ms,序列化之后的长度为:5175==============
16:29:32.798 [main] INFO com.whj.springboot.config.main - jdkSeri开始反序列化
16:29:32.977 [main] INFO com.whj.springboot.config.main - jdkSeri反序列化耗时:179ms===========
16:29:32.977 [main] INFO com.whj.springboot.config.main - genJsonSeri序列化开始
16:29:33.237 [main] INFO com.whj.springboot.config.main - genJsonSeri序列化结束,耗时:260ms,序列化之后的长度为:87025==============
16:29:33.237 [main] INFO com.whj.springboot.config.main - genJsonSeri开始反序列化
16:29:33.428 [main] INFO com.whj.springboot.config.main - genJsonSeri反序列化耗时:191ms===========
16:29:33.428 [main] INFO com.whj.springboot.config.main - jack2Seri序列化开始
16:29:33.436 [main] INFO com.whj.springboot.config.main - jack2Seri序列化结束,耗时:7ms,序列化之后的长度为:45001===============
16:29:33.436 [main] INFO com.whj.springboot.config.main - jack2Seri开始反序列化
16:29:33.451 [main] INFO com.whj.springboot.config.main - jack2Seri反序列化耗时:15ms=============

网上很多人分析说推荐使用GenericJackson2JsonRedisSerializer,但是通过上面的测试,我们可以得出结论:
1、就序列化和反序列化性能来说,Jackson2JsonRedisSerializer方式效率是最高的,同时在redis中也有很好的可读性
2、就占用存储空间来说,JdkSerializationRedisSerializer方式序列化之后长度是最小的(因为是用二进制方式存储)
相反,GenericJackson2JsonRedisSerializer方式没发现什么亮眼的特点(也可能我也是一知半解,欢迎讨论)

4、问题回答

回到我初始的问题,因为我的ValueSerializer使用的是StringRedisSerializer,在保存的时候将对象转换成了字符串保存,这样在redis中就是当做了普通的字符串,而不是对象。此时取出数据,使用fastJson转换成对象时,就会报上面的问题,因为它不是对象数据,而是个字符串。

5、JSON转换成对象

1、将json数据转换成对象,使用com.alibaba.fastjson.JSON.parseObject(str,User.class);
注:fastJson很强大,就算User对象中包含对象属性,也能转换成功
2、 将带有泛型的数据转换成对象,同样使用fastjson
JSON.parseObject(s,new TypeReference<User>(){})

6、修改RedisTemplate序列化方式

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        /**
         * 配置自己的redisTemplate
         * StringRedisTemplate 默认使用使用StringRedisSerializer来序列化
         * RedisTemplate 默认使用JdkSerializationRedisSerializer来序列化
         */
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        //开启默认类型
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //设置日期格式
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

猜你喜欢

转载自blog.csdn.net/u012031408/article/details/89478241
今日推荐