springboot2.x +redis使用和源码分析三(序列化器)

7 篇文章 0 订阅
3 篇文章 0 订阅

 

目录

1:spring-redis中提供的默认序列化器

1.1:StringRedisSerializer

1.2:JdkSerializationRedisSerializer

1.3:Jackson2JsonRedisSerializer

1.4:GenericJackson2JsonRedisSerializer

1.5:GenericToStringSerializer

1.6:自定义序列化器

2:各主要序列化器demo代码及序列化效率测试

3:总结:


1:spring-redis中提供的默认序列化器

了解spring提供的序列化器有助于我们针对不同需求使用最优的序列化器,虽然默认的JdkSerializationRedisSerializer基本可以满足我们的需求。

所有关于springboot有关的序列化器都在spring-data-redis包的serializer子包中,如图所示:

这里主要说明使用较多的序列化器:

1.1:StringRedisSerializer

StringRedisTemplate中默认序列化器,用于简单的string类型序列化

1.2:JdkSerializationRedisSerializer

用于序列化java对象,使用该序列化器,被序列化对象必须实现Serializable接口,最终落地到redis中数据不仅仅包含属性数据还包含一些类变量信息用于反序列化时可以正常转变为对象。所以导致可读性很差,并且落地redis的数据体量较大,浪费redis内存资源。

1.3:Jackson2JsonRedisSerializer

将java中Object序列化为json,最终保存到redis中数据为json字符串,但是使用该序列化器依赖于jackson,本质是通过jackson中objectMapper对象提供的writeValueAsBytes()与readValue()来进行序列化与反序列化。因为落地redis中数据是json字符串,所以可读性好,最终数据体量较小。但该序列化器存在一个缺点无法反序列化类型为List(或其它集合类型)带有泛型的对象。Jackson2JsonRedisSerializer进行反序列化list时其中存放是LinkedHashMap,所以当我们使用带有泛型的对象的list接受时就会出现java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to ... 类型转换异常。

1.4:GenericJackson2JsonRedisSerializer

和上述Jackson2JsonRedisSerializer相同点在于:也是将java中对象序列化为json,也是依赖与jackson,本质也是通过jackson中objectMapper对象提供的writeValueAsBytes()与readValue(里面参数不一样)来进行序列化与反序列化。而不同在于GenericJackson2JsonRedisSerializer在处理存储的类型为List(或其它集合类型)等带有泛型的对象时可以正常反序列,这是因为GenericJackson2JsonRedisSerializer在序列化时会将一些反序列化时的类信息保存在其中,而Jackson2JsonRedisSerializer只会存储json原数据,也是因为多加这写类信息导致在序列化的速度是要低于Jackson2JsonRedisSerializer

1.5:GenericToStringSerializer

 和StringRedisSerializer作用类型,不同在于GenericToStringSerializer可以将java对象现转换为字符串之后再进行序列化,而StringRedisSerializer对于需要进行序列只能要求为string类型。但经过测试发现,虽然GenericToStringSerializer入参中可以指定对象类型,但目前默认只正常8大基础类型转换,若入参为java对象则发生org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [。。。] to type [java.lang.String]异常

1.6:自定义序列化器

实现RedisSerializer接口,自己来实现适用于本身需求的序列器

2:各主要序列化器demo代码及序列化效率测试

测试代码:

 /**
     * 使用不同序列化器存储redis数据
     * @return
     */
    @RequestMapping("serializerTest")
    public  Map<String,Object> serializerTest(){

        PersonInfo personInfo = new PersonInfo();
        personInfo.setUserId("1");
        personInfo.setAge(18);
        personInfo.setName("小花");
        personInfo.setSex("女");

        List<PersonInfo> personInfos = Collections.singletonList(personInfo);

        String keyName = "serializerTest_";

        //使用StringSerializer序列话key

        StopWatch stopWatch = new StopWatch();

        redisTemplate.setKeySerializer(new StringRedisSerializer());

        String taskName = "task_";
        stopWatch.start(taskName+"StringRedisSerializer");
        for (int i=0;i<100;i++){
            //StringRedisSerializer 序列化
            redisTemplate.setValueSerializer(new StringRedisSerializer());
            redisTemplate.opsForValue().set(keyName+"StringRedisSerializer"+i, JSONObject.toJSONString(personInfos));
        }
        stopWatch.stop();

        stopWatch.start(taskName+"Jackson2JsonRedisSerializer");
        for (int i=0;i<100;i++){
            //使用Jackson2JsonRedisSerializer序列器 序列化
            redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
            redisTemplate.opsForValue().set(keyName+"Jackson2JsonRedisSerializer"+i,personInfos);
        }
        stopWatch.stop();

        stopWatch.start(taskName+"JdkSerializationRedisSerializer");
        for (int i=0;i<100;i++){
            //使用JdkSerializationRedisSerializer序列器 序列化
            redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
            redisTemplate.opsForValue().set(keyName+"JdkSerializationRedisSerializer"+i,personInfos);
        }
        stopWatch.stop();

        stopWatch.start(taskName+"GenericJackson2JsonRedisSerializer");
        for (int i=0;i<100;i++){
            //使用GenericJackson2JsonRedisSerializer序列器 序列化
            redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
            redisTemplate.opsForValue().set(keyName+"GenericJackson2JsonRedisSerializer"+i,personInfos);
        }
        stopWatch.stop();

        stopWatch.start(taskName+"GenericToStringSerializer");
        for (int i=0;i<100;i++){
            //GenericToStringSerializer序列化器 序列化
            redisTemplate.setValueSerializer(new GenericToStringSerializer<String>(String.class));
            //s
            redisTemplate.opsForValue().set(keyName+"GenericToStringSerializer"+i, JSONObject.toJSONString(personInfos));
        }
        stopWatch.stop();

        //打印使用不同序列化器执行时间结果
        System.out.println(stopWatch.prettyPrint());

        Map<String,Object> map = new HashMap<>(2);
        map.put("success",true);
        map.put("data",1);

        return map;
    }

上述测试代码测试了5种序列化器的下的执行效率,每次写入100次数据,随机测试5次得到以下结果:

第一次:
StopWatch '': running time = 1757843969 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
394704271  022%  task_StringRedisSerializer
454614614  026%  task_Jackson2JsonRedisSerializer
264084771  015%  task_JdkSerializationRedisSerializer
361995244  021%  task_GenericJackson2JsonRedisSerializer
282445069  016%  task_GenericToStringSerializer
第二次:
StopWatch '': running time = 1436306976 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
267357677  019%  task_StringRedisSerializer
332845988  023%  task_Jackson2JsonRedisSerializer
257288580  018%  task_JdkSerializationRedisSerializer
322376729  022%  task_GenericJackson2JsonRedisSerializer
256438002  018%  task_GenericToStringSerializer
第三次:
StopWatch '': running time = 1553815955 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
274423175  018%  task_StringRedisSerializer
350550436  023%  task_Jackson2JsonRedisSerializer
255580651  016%  task_JdkSerializationRedisSerializer
364450737  023%  task_GenericJackson2JsonRedisSerializer
308810956  020%  task_GenericToStringSerializer
第四次:
StopWatch '': running time = 1393176866 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
280269100  020%  task_StringRedisSerializer
300631014  022%  task_Jackson2JsonRedisSerializer
246369052  018%  task_JdkSerializationRedisSerializer
310196075  022%  task_GenericJackson2JsonRedisSerializer
255711625  018%  task_GenericToStringSerializer
第五次:
StopWatch '': running time = 1283761835 ns
---------------------------------------------
ns         %     Task name
---------------------------------------------
242299969  019%  task_StringRedisSerializer
270059852  021%  task_Jackson2JsonRedisSerializer
238653677  019%  task_JdkSerializationRedisSerializer
279510428  022%  task_GenericJackson2JsonRedisSerializer
253237909  020%  task_GenericToStringSerializer

从上述接口可以看出 执行效率为:JdkSerializationRedisSerializer>GenericToStringSerializer>StringRedisSerializer>GenericJackson2JsonRedisSerializer>Jackson2JsonRedisSerializer

3:总结:

  1. 如果我们要求低延时不考虑redis内存空间可以使用JdkSerializationRedisSerializer

  2. 如果redis的内存空间很宝贵而不追求低延时可以考虑Jackson2JsonRedisSerializer或GenericToStringSerializer或StringRedisSerializer,当然后两种需求我们先将对象自己转变为json格式
  3. 如果取两种中间态可以考虑使用GenericToStringSerializer序列化器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值