1. 先從序列化說起
首先什么是序列化椿息,簡單地來說序列化就是將對象轉換成字節(jié)序列可训,反序列化就是將字節(jié)序列轉化成對象仓坞。
2. RedisTemplate
Redis作為項目中常用的緩存中間件帆阳,我們通常使用spring-data-redis中提供了RedisTemplate來操作Redis迎吵。而在初始化RedisTemplate需要進行設置key/value的序列化方式嗤形,那么我們應該如何設置對應的序列化方式精偿?
@Bean(name = {"qualityRedisTemplate"})
public RedisTemplate<String, Object> qualityRedisTemplate(JedisConnectionFactory qualityConFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
template.setConnectionFactory(qualityConFactory);
template.setKeySerializer(jdkSerializationRedisSerializer);
template.setValueSerializer(jdkSerializationRedisSerializer);
return template;
}
3. 序列化方式
spring-data-redis的序列方式是通過實現(xiàn)RedisSerializer接口,默認實現(xiàn)了6種序列化方式赋兵,分別是GenericToStringSerializer笔咽、Jackson2JsonRedisSerializer、JdkSerializationRedisSerializer霹期、OxmSerializer叶组、StringRedisSerializer、GenericJackson2JsonRedisSerializer历造。
3.1 JdkSerializationRedisSerializer
這種序列化方式是RedisTemplate默認的序列化方式甩十,使用這種序列化方式的前提:被序列化的對象必須實現(xiàn)了java.io.Serializable接口。這種方式序列化之后保存的是字節(jié)序列吭产,對于開發(fā)調(diào)試不太友好侣监,使用Redis客戶端打開都是亂碼的。
3.2 StringRedisSerializer
這種序列化方式是StringRedisTemplate默認的序列化方式臣淤。這種實現(xiàn)方式比較輕量級橄霉、效率比較高,但是這種方式只能對字符串進行序列化邑蒋,無法對普通對象進行序列化姓蜂。如果想對普通對象進行序列化按厘,必須先手動轉化成String格式。
3.3 GenericToStringSerializer
這種序列化方式與StringRedisSerializer非常相似钱慢,只能對八種基本類型進行序列化刻剥。如果對普通對象進行序列化必須另外實現(xiàn)ConversionService或者TypeConverter。
3.4 Jackson2JsonRedisSerializer
這種序列化方式從名字就可以看出來是通過將對象序列化成json串進行存儲的滩字,這種存儲方式對于開發(fā)調(diào)試比較友好造虏,排查線上問題也更加方便。但是這種序列化方式麦箍,必須在構造序列化對象時傳入對象的類型漓藕,否則會導致反序列化錯誤。
3.5 GenericJackson2JsonRedisSerializer
這種序列化方式是對Jackson2JsonRedisSerializer的一種補充挟裂,也是將對象序列化成json串進行存儲享钞,序列化時將對象類型也保存了,所以反序列化時無需傳入對象類型诀蓉,這種實現(xiàn)方式顯然更好栗竖。
// 使用jackson或者genericJackson存在的坑
redisTemplate.opsForValue().set(key, 10L);
Long value = redisTemplate.opsForValue().get(key);
// 此時獲得的值的類型為Integer類型, 直接進行強轉會進行報錯
// 其實這也不能說是序列化存在的問題,而是json的數(shù)字類型與java的數(shù)據(jù)類型不能兼容
3.6 OxmSerializer
這種序列化方式是通過將對象序列化成xml的格式進行存儲渠啤,使用xml存儲格式比較統(tǒng)一規(guī)范狐肢。但是序列化成xml與反序列化成對象,都是比較耗時的沥曹。
4. 對比序列化方式
序列化方式 | 序列化后大小 | 序列化時間 | 反序列化時間 | 可讀性 |
---|---|---|---|---|
jdk | 615 | 0.01 | 0.016 | 差 |
jackson | 337 | 0.005 | 0.01 | 好 |
genericJackson | 425 | 0.003 | 0.005 | 好 |
genericString | 283 | 0.006 | 0.008 | 好 |
oxm | 481 | 0.031 | 0.045 | 一般 |
注:序列化時間與反序列化時間是分別執(zhí)行100次得出的時間
public void timeCost() {
RedisSerializer redisSerializer = redisTemplate.getValueSerializer();
LocalDateTime start = LocalDateTime.now();
byte[] serialize = new byte[]{};
for (int i = 0; i < 100; i++) {
serialize = redisSerializer.serialize(activity);
}
LocalDateTime end = LocalDateTime.now();
System.out.println("序列化耗時:" + Duration.between(start, end));
start = LocalDateTime.now();
for (int i = 0; i < 100; i++) {
redisSerializer.deserialize(serialize);
}
end = LocalDateTime.now();
System.out.println("反序列化耗時:" + Duration.between(start, end));
}
5. 使用中存在的坑
5.1 JdkSerializationRedisSerializer
// jdk序列化方式存在的坑
redisTemplate.opsForValue().set(key, 10);
redisTemplate.opsForValue().increment(key); // 會拋出ERR value is not an integer or out of range 異常
5.2 Jackson2JsonRedisSerializer以及GenericJackson2JsonRedisSerializer
// 使用jackson或者genericJackson存在的坑
redisTemplate.opsForValue().set(key, 1L);
Long value = redisTemplate.opsForValue().get(key);
// 此時獲得的值的類型為Integer類型, 直接進行強轉會進行報錯
// 其實這也不能說是序列化存在的問題份名,而是json的數(shù)字類型與java的數(shù)據(jù)類型不能兼容
6. 其他第三方實現(xiàn)的序列化方式
- GenericFastJsonRedisSerializer
- KryoRedisSerializer