Redis序列化学习

Redis序列化学习

Posted by yyconstantine on December 30, 2019

Redis序列化学习

1.为什么需要对Redis实现自定义序列化

首先看一下Spring对Spring Data Redis序列化器给出的解释:

从框架的角度来看,Redis中存储的数据仅为字节。虽然Redis本身支持各种类型,但在大多数情况下,它们是指数据的存储方式,而不是其表示的内容。由用户决定是否将信息转换为字符串或任何其他对象。

其中包括JdkSerializationRedisSerializer,默认用于RedisCacheRedisTemplate

说白了,Redis的序列化是为了我们存储的信息可以通过网络IO的方式进行传输,而不拘泥于平台的限制。

而Redis默认的JdkSerializationRedisSerializer需要我们传输的对象实现Serializable接口,并且序列化后的结果十分庞大, 会大量占用Redis内存;而且使用JdkSerializationRedisSerializer后,序列化后的结果在Redis-Cli工具中查看会出现乱码,十分不方便,所以不推荐使用。


2.都有哪些常见的自定义序列化方式

  • StringRedisSerializer:一般用于key的存储,顾名思义这是将数据采用String格式存储和接收,当我们的key大多为String时可采用此种序列化方式
  • Jackson2JsonRedisSerializer / FastJsonRedisSerializer:一般用于value的存储,将数据存储为json类型
  • GenericJackson2JsonRedisSerializer:与Jackson2JsonRedisSerializer大致相同,会额外存储序列化对象的包命和类名
  • 使用Kryo / FST:在这里不再赘述,Kryo个人认为是一种更新更快的序列化方式,不过需要手动创建线程池做线程安全处理,及注册序列化的基本数据类型/对象,详情可参照: https://juejin.im/post/5d5e10d2e51d4561b416d487

说完了常见的自定义序列化方式,再简单说下如何使用自定义的序列化方式:

@Configuration
public clas RedisSerializeConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        
        // 创建StringRedisSerializer
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        // 将key的序列化方式指定为StringRedisSerializer
        redisTemplate.setKeySerializer(stringSerializer);
        // 创建Jackson2JsonRedisSerializer
        Jackson2JsonRedisSerializer jsonSerializer = new Jackson2JsonRedisSerializer();
        // 将value的序列化方式指定为Jackson2JsonRedisSerializer
        redisTemplate.setValueSerializer(jsonSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
    
}

3.个人遇到的问题

由于在项目中使用redis遇到了使用key-value存储汉字的时候出现乱码问题,所以采用了自定义序列化方式,对key采用StringRedisSerializer,对value采用Jackson2JsonRedisSerializer

由于我负责的主要是交易模块的开发,涉及到扫码支付,直接请求接口的时候无法使用二维码序列,所以会去redis-cli中手动set一些数据。当使用简单的key-value如1、2、3是正常通过的,当我使用uuid存储为value时出现问题,抛出Redis反序列化异常了:

  • nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('a' (code 97)): Expected space separating root-level values

最开始看错误提示是困惑的,因为之前一直没遇到问题,突然遇到了问题以为是value数据类型的问题导致的,对value使用StringRedisSerializer进行序列化的确是OK的,但实际项目中肯定不能将value都设置为String类型接收,会造成不必要的麻烦。

最后还是这篇博客给了我启发,原来是我在cli工具走的是默认的JdkRedisSerializer,而项目中使用Jackson2JsonRedisSerializer,序列化方式不同造成了无法进行反序列化,而使用接口请求获取的key-value则可以正常通过反序列化。