在Spring Data Redis中,RedisTemplate 是操作Redis的核心類,它提供了豐富的API來與Redis進行交互。由于Redis是一個鍵值存儲系統,它存儲的是字節序列,因此在使用RedisTemplate時,需要指定鍵(Key)和值(Value)的序列化方式。不同的序列化方式適用于不同的場景。下面將詳細介紹幾種序列化方法。
序列化如下對象
User 類
public class User implements Serializable {String name;String ID;@Overridepublic 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
- 序列化生成的字節序列較大,導致網絡傳輸和存儲成本較高。
- 序列化速度慢。
- 序列化的字節序列是私有的,不便于跨語言或跨平臺共享。
代碼示例
@Testpublic 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庫,建議生產中別用!。
@Testpublic 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
@Testpublic 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
@Testpublic 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
@Testpublic 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());}