在Spring Data Redis中,RedisTemplate 是操作Redis的核心类,它提供了丰富的API来与Redis进行交互。由于Redis是一个键值存储系统,它存储的是字节序列,因此在使用RedisTemplate时,需要指定键(Key)和值(Value)的序列化方式。不同的序列化方式适用于不同的场景。下面将详细介绍几种序列化方法。
序列化如下对象
User 类
public class User implements Serializable {
String name;
String ID;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", ID='" + ID + '\'' +
'}';
}
public User(String name, String ID) {
this.name = name;
this.ID = ID;
}
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getID() {
return ID;
}
public void setID(String ID) {
this.ID = ID;
}
}
JdkSerializationRedisSerializer
JdkSerializationRedisSerializer
是使用JDK自带的序列化机制(ObjectOutputStream 和 ObjectInputStream
)来序列化和反序列化POJO对象。这种序列化方式会将对象转换成字节序列,并存储在Redis中。这是RedisTemplate中默认的序列化策略之一(但通常不是推荐用于生产环境的默认策略,因为JDK序列化通常效率较低且生成的字节序列较大)。最大的缺点就是:要求序列化的对象要求继承Serializable
类,这是DTO无法容忍的一个要求。
优点:
- 与其他两个比几乎没有优点,超级不推荐!
缺点:
- 二进制形式存储,不利于查看!94 bytes
- 序列化生成的字节序列较大,导致网络传输和存储成本较高。
- 序列化速度慢。
- 序列化的字节序列是私有的,不便于跨语言或跨平台共享。
代码示例
@Test
public void test4(){
User user = new User("李白","123456");
// GenericToStringSerializer<Object> genericToStringSerializer = new GenericToStringSerializer<>(Object.class);
// redisTemplate.setKeySerializer(genericToStringSerializer);
// JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
// redisTemplate.setValueSerializer(jdkSerializationRedisSerializer);
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.java());
// 记录开始时间
Instant start = Instant.now();
redisTemplate.opsForValue().set("test4",user);
User user2 = (User)redisTemplate.opsForValue().get("test4");
logger.info(user2.toString());
// 记录结束时间
Instant end = Instant.now();
// 计算运行时间
long duration = Duration.between(start, end).toMillis();
logger.info(duration+"ms");
}
StringRedisSerializer
StringRedisSerializer
是最简单的序列化器,它直接将字符串(或任何可以转换为字符串的数据)作为字节序列存储,不需要进行任何特殊的序列化操作。它适用于键或值为字符串的场景。
优点:
- 效率高,不需要额外的序列化/反序列化开销。
- 易于理解和维护。
缺点:
- 只能用于字符串数据。
Jackson2JsonRedisSerializer
Jackson2JsonRedisSerializer
是基于Jackson
库实现的JSON序列化器,它可以将Java对象序列化成JSON格式的字符串,并存储在Redis中。Jackson是一个流行的JSON处理库,提供了丰富的API来序列化和反序列化Java对象。
优点:
- User 对象不需要实现
Serializable
接口。 - 生成的JSON格式易于阅读和调试。
- 序列化后的数据相对较小,传输和存储效率较高,64 bytes。
- 支持复杂的Java对象,包括嵌套对象和集合。
@Test
public void test3(){
User user = new User("李白","123456");
redisTemplate.setKeySerializer(RedisSerializer.string());
// Jackson2JsonRedisSerializer<User> userJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(User.class);
// redisTemplate.setValueSerializer(userJackson2JsonRedisSerializer);
redisTemplate.setValueSerializer(RedisSerializer.json());
// 记录开始时间
Instant start = Instant.now();
redisTemplate.opsForValue().set("test3",user);
User user2 = (User)redisTemplate.opsForValue().get("test3");
logger.info(user2.toString());
// 记录结束时间
Instant end = Instant.now();
// 计算运行时间
long duration = Duration.between(start, end).toMillis();
logger.info(duration+"ms");
}
GenericFastJsonRedisSerializer
GenericFastJsonRedisSerializer
是基于Fastjson库实现的JSON序列化器,与JacksonJsonRedisSerializer
类似,但它使用的是Fastjson库。Fastjson是另一个流行的JSON处理库,以其高性能著称。
优点:
- 序列化性能高。
- 支持复杂的Java对象。
- 生成的JSON格式易于阅读和调试。
缺点:
- 可能存在安全漏洞(历史版本中曾发现过安全问题,使用时需注意版本),据测试,FastJson性能不能完全超过Json库,建议生产中别用!。
@Test
public void test5(){
User user = new User("杜甫","123456");
redisTemplate.setKeySerializer(RedisSerializer.string());
GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);
// 记录开始时间
Instant start = Instant.now();
redisTemplate.opsForValue().set("test5",user);
User user2 = (User)redisTemplate.opsForValue().get("test5");
logger.info(user2.toString());
// 记录结束时间
Instant end = Instant.now();
// 计算运行时间
long duration = Duration.between(start, end).toMillis();
logger.info(duration+"ms");
}
总结
- 可读性:
JacksonJsonRedisSerializer
和GenericFastJsonRedisSerializer
皆可 - 内存占用:
GenericFastJsonRedisSerializer
59 bytes 小于JacksonJsonRedisSerializer
60 bytes 小于JdkSerializationRedisSerializer
94 bytes。 - 耗时:
JacksonJsonRedisSerializer
和GenericFastJsonRedisSerializer
差不多 且 都比JdkSerializationRedisSerializer
。
生产环境中,无脑选择JacksonJsonRedisSerializer
即可!
总的来说,在选择序列化器时,应根据具体的应用场景和需求来决定使用哪种序列化方式。对于大多数基于Spring Boot和Spring Data Redis的项目,推荐使用JacksonJsonRedisSerializer
来序列化和反序列化Java对象,因为它们提供了灵活性和高性能。
嵌套对象测试
public Map<String, List<User>> getNestedObj(){
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 100; i++) {
users.add(new User(UUID.randomUUID().toString().substring(0,8),UUID.randomUUID().toString()));
}
HashMap<String, List<User>> nestedObj = new HashMap<>();
nestedObj.put("one",users);
return nestedObj;
}
JdkSerializationRedisSerializer
@Test
public void test11(){
Map<String, List<User>> nestedObj = getNestedObj();
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.java());
// 记录开始时间
Instant start = Instant.now();
redisTemplate.opsForValue().set("test11",nestedObj);
// 记录结束时间
Instant end = Instant.now();
// 计算运行时间
long duration = Duration.between(start, end).toMillis();
logger.info(duration+"ms");
// 从redis获取nestedObj并反序列化
Map<String, List<User>> nestedObj2 = (Map<String, List<User>>)redisTemplate.opsForValue().get("test11");
logger.info(nestedObj2.toString());
}
JacksonJsonRedisSerializer
@Test
public void test12(){
Map<String, List<User>> nestedObj = getNestedObj();
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.json());
// 记录开始时间
Instant start = Instant.now();
redisTemplate.opsForValue().set("test12",nestedObj);
// 记录结束时间
Instant end = Instant.now();
// 计算运行时间
long duration = Duration.between(start, end).toMillis();
logger.info(duration+"ms");
// 从redis获取nestedObj并反序列化
Map<String, List<User>> nestedObj2 = (Map<String, List<User>>)redisTemplate.opsForValue().get("test12");
logger.info(nestedObj2.toString());
}
GenericFastJsonRedisSerializer
@Test
public void test13(){
Map<String, List<User>> nestedObj = getNestedObj();
redisTemplate.setKeySerializer(RedisSerializer.string());
GenericFastJsonRedisSerializer genericFastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
redisTemplate.setValueSerializer(genericFastJsonRedisSerializer);
// 记录开始时间
Instant start = Instant.now();
redisTemplate.opsForValue().set("test13",nestedObj);
// 记录结束时间
Instant end = Instant.now();
// 计算运行时间
long duration = Duration.between(start, end).toMillis();
logger.info(duration+"ms");
// 从redis获取nestedObj并反序列化
Map<String, List<User>> nestedObj2 = (Map<String, List<User>>)redisTemplate.opsForValue().get("test13");
logger.info(nestedObj2.toString());
}