SpringDataRedis 的序列化方式总结

问题引入

当我们使用 RedisTemplate 向Redis 中 写入一条String数据时,可以正常插入获取数据

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void testString() {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        valueOperations.set("name","xiaotengteng");		//打断点追源码
        System.out.println("name = "+valueOperations.get("name"));
    }

但打开Redis 客户端查看,或者 redis-cli 查看时发现:

1674482108810

插入的数据被序列化为字节形式

1674482696263

原因

RedisTemplate可以接收任意Object作为值写入Redis:

OEMcbuu

只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化工具:ObjectOutputStream,所以得到以上结果,尝试追一下源码:

进入 RedisTemplate 类里面:keySerializer,valueSerializer,hashKeySerializer,hashValueSerializer,普通字符串和 hash结构的 序列化器,来实现序列化和反序列化

1674484237279

默认 Jdk 序列化器

1674484416134

查看源码

在 set 方法上打断点,debug 追源码:

传进来的值变为字节

1674483314408

获取值的序列化器

1674483412612

进入到默认的 JDK 序列化器:JdkSerializationRedisSerializer

1674483560527

接着往下

1674483612044

缓冲

1674483738228

把 java对象转为 字节 写入 Redis:ObjectOutputStream,也就是我们看到的那长串字节

1674483914751

这种序列化方式的缺点:

  • 可读性差
  • 内存占用较大

其他序列化器:

  • key 通常为 字符串,可以使用:StringRedisSerializer
  • value 通常为 object 对象,可以使用:Jackson2JsonRedisSerializer

请添加图片描述

解决办法

自定义RedisTemplate的序列化

key:String & value:String

新建 RedisConfig 配置类

package com.itheima.springdataredisdemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
        //创建 RedisTemplate 对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        //设置连接工厂
        template.setConnectionFactory(connectionFactory);
        //创建 JSON 序列化工具
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        //设置 key 的序列化
        template.setKeySerializer(RedisSerializer.string());
        template.setHashKeySerializer(RedisSerializer.string());
        //设置 value 的序列化
        template.setValueSerializer(jsonRedisSerializer);
        template.setHashValueSerializer(jsonRedisSerializer);
        //返回
        return template;
    }
}

测试

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Test
    void testString() {

        ValueOperations valueOperations = redisTemplate.opsForValue();
        valueOperations.set("name","xiaotengteng");
        System.out.println("name = "+valueOperations.get("name"));
    }

结果报错,错误信息如下:

Caused by: java.lang.ClassNotFoundException: com.fasterxml.jackson.core.JsonProcessingException
//需要添加依赖

pom.xml

        <!--Jackson依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>

测试:修改 name 成功

1674570714996

key:String & value:Object

新建 User 类

package com.itheima.springdataredisdemo.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    
    private String name;
    
    private Integer age;
}

测试

    @Test
    void testSaveUser(){
        redisTemplate.opsForValue().set("user",new User("杰克",21));
        User user = (User) redisTemplate.opsForValue().get("user");
        System.out.println(user);
    }

这里采用了JSON序列化来代替默认的JDK序列化方式。最终结果如图:

1674571441366

整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。

StringRedisTemplate

概述

为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。

Ip9TKSY

因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis就不会将class信息写入Redis了。

这种用法比较普遍,因此SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。

image-20230131150855477

代码实现

@SpringBootTest
class RedisStringTests {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    void testString() {
        // 写入一条String数据
        stringRedisTemplate.opsForValue().set("verify:phone:13600527634", "124143");
        // 获取string数据
        Object name = stringRedisTemplate.opsForValue().get("name");
        System.out.println("name = " + name);
    }

    private static final ObjectMapper mapper = new ObjectMapper();

    @Test
    void testSaveUser() throws JsonProcessingException {
        // 创建对象
        User user = new User("虎哥", 21);
        // 手动序列化
        String json = mapper.writeValueAsString(user);
        // 写入数据
        stringRedisTemplate.opsForValue().set("user:200", json);

        // 获取数据
        String jsonUser = stringRedisTemplate.opsForValue().get("user:200");
        // 手动反序列化
        User user1 = mapper.readValue(jsonUser, User.class);
        System.out.println("user1 = " + user1);
    }

}

测试

此时我们再来看一看存储的数据,发现那个class数据已经不在了,节约了我们的空间~

image-20230131151050169

总结

RedisTemplate的两种序列化实践方案:

  • 方案一:

    • 自定义RedisTemplate
    • 修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer
  • 方案二:

    • 使用StringRedisTemplate
    • 写入Redis时,手动把对象序列化为JSON
    • 读取Redis时,手动把读取到的JSON反序列化为对象
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GaleTeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值