SpringDataRedis存储Redis的数据序列化
在使用Spring Data Redis存储数据至Redis时,选择合适的序列化策略至关重要。它不仅影响数据存储的效率和空间利用率,还关系到跨语言兼容性和系统的扩展性。适当的序列化方式可以确保数据正确无误地被存储和读取,提升系统的稳定性和维护性,避免由于默认序列化带来的乱码或不兼容问题,从而保障应用的高效运行和数据的安全完整。
目录
序列化介绍
问题解析
问题测试
解决方案
方案一
方案二
序列化介绍
序列化是指将对象的状态信息转换为可以存储或传输的形式的过程。在反序列化时,这个过程是相反的,即将这些信息还原成对象。
当你将数据存储到Redis中时,如果发现数据变成字节,这通常与序列化方式有关。指在Java环境中使用Redis客户端,默认情况下会使用JDK内置的序列化方式(即Serializable
接口及ObjectOutputStream
等类来实现对象的序列化)。
问题解析
兼容性问题:JDK序列化格式是Java特有的,这意味着只有Java程序才能反序列化这些对象。如果你的系统中有非Java组件需要访问Redis中的数据,那么它们将无法直接读取这些序列化的对象。
性能问题:JDK序列化机制有时会比较慢,并且生成的序列化内容可能比其他序列化方法(如JSON, Protobuf, Avro等)更大,从而影响网络传输效率和存储空间。
安全性考虑:反序列化不受信任的数据源可能导致安全漏洞,例如反序列化攻击。这是因为反序列化过程会执行某些代码,如果攻击者能够控制输入流,就可能利用这一点执行恶意代码.
其实可读性也非常差(这谁能看出来name=小明, age=18是上面这一大串)
问题测试
使用SpringDataRedis客户端:
创建一个测试的SpringBoot项目,引入依赖:
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在application.yaml加入:
spring:
data:
redis:
host: localhost
port: 6379
password:
lettuce:
pool:
max-active: 8
min-idle: 0
max-idle: 8
max-wait: 1000
在测试类,加入以下代码:
@SpringBootTest class SpringDataRedisDemoApplicationTests { @Autowired private RedisTemplate redisTemplate; @Test void testString() { redisTemplate.opsForValue().set("time","12"); Object time = redisTemplate.opsForValue().get("time"); System.out.println("time = " + time); } @Test void testUser() { redisTemplate.opsForValue().set("user",new User("小明",18)); User user = (User) redisTemplate.opsForValue().get("user"); System.out.println("user = " + user); } }
运行后查看Java客户端工具:
解决方案
方案一
配置RedisTemplate,
使用JSON作为序列化格式,实现自动序列化和反序列化。
加入下列代码,设置特定的序列化工具(GenericJackson2JsonRedisSerializer
)
@Configuration
public class RedisConfig {
/**
* RedisTemplate配置序列化规则
* @param connectionFactory
* @return
*/
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory) {
// 创建RedisTemplate对象
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
// 设置连接工厂
redisTemplate.setConnectionFactory(connectionFactory);
// 创建Json序列化工具
GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
// 设置key的序列化规则
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
// 设置value的序列化规则
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
}
修改测试类注入的RedisTemplate类型
@Autowired
private RedisTemplate<String,Object> redisTemplate;
运行结果如下:
可以看到可读性大大提高,可以确保所有存储到Redis中的数据都以一种一致且易于理解的方式进行序列化和反序列化,提升了与Redis交互时的序列化效率、数据兼容性和易用性,使得存储和检索过程更加高效和直观。
方案二
方案一有一个缺点,在储存对象时,把类的class类型写入了JSON结果中,存入Redis,带来了额外的内存开销。(在大量数据的情况下可能给内存带来压力)
使用StringRedisTemplate,手动实现序列化和反序列化。
@Autowired
private StringRedisTemplate stringRedisTemplate;
private static final ObjectMapper mapper = new ObjectMapper();
@Test
void testUser() throws JsonProcessingException {
//创建对象
User user = new User("张三", 18);
// 手动序列化
String json = mapper.writeValueAsString(user);
// 存入redis
stringRedisTemplate.opsForValue().set("users",json);
// 取出数据
String users = stringRedisTemplate.opsForValue().get("users");
// 手动反序列化
User user1 = mapper.readValue(users, User.class);
// 输出
System.out.println("user1 = " + user1);
}
实现效果如下:
实现了去除类的class类型,节省了内存。