在使用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: localhostport: 6379password:lettuce:pool:max-active: 8min-idle: 0max-idle: 8max-wait: 1000
在測試類,加入以下代碼:
@SpringBootTest class SpringDataRedisDemoApplicationTests {@Autowiredprivate RedisTemplate redisTemplate;@Testvoid testString() {redisTemplate.opsForValue().set("time","12");Object time = redisTemplate.opsForValue().get("time");System.out.println("time = " + time);}@Testvoid 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*/@Beanpublic 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類型
@Autowiredprivate RedisTemplate<String,Object> redisTemplate;
運行結果如下:
可以看到可讀性大大提高,可以確保所有存儲到Redis中的數據都以一種一致且易于理解的方式進行序列化和反序列化,提升了與Redis交互時的序列化效率、數據兼容性和易用性,使得存儲和檢索過程更加高效和直觀。
方案二
方案一有一個缺點,在儲存對象時,把類的class類型寫入了JSON結果中,存入Redis,帶來了額外的內存開銷。(在大量數據的情況下可能給內存帶來壓力)
使用StringRedisTemplate,手動實現序列化和反序列化。
@Autowiredprivate StringRedisTemplate stringRedisTemplate;private static final ObjectMapper mapper = new ObjectMapper();@Testvoid testUser() throws JsonProcessingException {//創建對象User user = new User("張三", 18);// 手動序列化String json = mapper.writeValueAsString(user);// 存入redisstringRedisTemplate.opsForValue().set("users",json);// 取出數據String users = stringRedisTemplate.opsForValue().get("users");// 手動反序列化User user1 = mapper.readValue(users, User.class);// 輸出System.out.println("user1 = " + user1);}
實現效果如下:
實現了去除類的class類型,節省了內存。