一、redisTemplate和stringRedisTemplate對比
RedisTemplate看這個類的名字后綴是Template,如果了解過Spring如何連接關系型數據庫的,大概不會難猜出這個類是做什么的 ,它跟JdbcTemplate一樣封裝了對Redis的一些常用的操作,當然StringRedisTemplate跟RedisTemplate功能類似那么肯定就會有人問,為什么會需要兩個Template呢,一個不就夠了嗎?其實他們兩者之間的區別主要在於他們使用的序列化類。
RedisTemplate使用的是 JdkSerializationRedisSerializer 序列化對象
StringRedisTemplate使用的是 StringRedisSerializer 序列化String
1、StringRedisTemplate
- 主要用來存儲字符串,StringRedisSerializer的泛型指定的是String。當存入對象時,會報錯 :can not cast into String。
- 可見性強,更易維護。如果過都是字符串存儲可考慮用StringRedisTemplate。
2、RedisTemplate
- 可以用來存儲對象,但是要實現Serializable接口。
- 以二進制數組方式存儲,內容沒有可讀性。
二、redisTemplate序列化方式比較
那有沒有辦法,可以序列化對象,可讀性又強呢?
- 1、手動轉化成json串再存儲。取出數據需要反序列化。
- 2、使用其他序列化方式。
spring-data-redis提供如下幾種選擇:
- GenericToStringSerializer: 可以將任何對象泛化為字符串並序列化
- Jackson2JsonRedisSerializer: 跟JacksonJsonRedisSerializer實際上是一樣的
- JacksonJsonRedisSerializer: 序列化object對象為json字符串
- JdkSerializationRedisSerializer: 序列化java對象
- StringRedisSerializer: 簡單的字符串序列化
1、性能測試對比
@Test public void testSerial(){ UserPO userPO = new UserPO(1111L,"小明_testRedis1",25); List<Object> list = new ArrayList<>(); for(int i=0;i<200;i++){ list.add(userPO); } JdkSerializationRedisSerializer j = new JdkSerializationRedisSerializer(); GenericJackson2JsonRedisSerializer g = new GenericJackson2JsonRedisSerializer(); Jackson2JsonRedisSerializer j2 = new Jackson2JsonRedisSerializer(List.class); Long j_s_start = System.currentTimeMillis(); byte[] bytesJ = j.serialize(list); System.out.println("JdkSerializationRedisSerializer序列化時間:"+(System.currentTimeMillis()-j_s_start) + "ms,序列化后的長度:" + bytesJ.length); Long j_d_start = System.currentTimeMillis(); j.deserialize(bytesJ); System.out.println("JdkSerializationRedisSerializer反序列化時間:"+(System.currentTimeMillis()-j_d_start)); Long g_s_start = System.currentTimeMillis(); byte[] bytesG = g.serialize(list); System.out.println("GenericJackson2JsonRedisSerializer序列化時間:"+(System.currentTimeMillis()-g_s_start) + "ms,序列化后的長度:" + bytesG.length); Long g_d_start = System.currentTimeMillis(); g.deserialize(bytesG); System.out.println("GenericJackson2JsonRedisSerializer反序列化時間:"+(System.currentTimeMillis()-g_d_start)); Long j2_s_start = System.currentTimeMillis(); byte[] bytesJ2 = j2.serialize(list); System.out.println("Jackson2JsonRedisSerializer序列化時間:"+(System.currentTimeMillis()-j2_s_start) + "ms,序列化后的長度:" + bytesJ2.length); Long j2_d_start = System.currentTimeMillis(); j2.deserialize(bytesJ2); System.out.println("Jackson2JsonRedisSerializer反序列化時間:"+(System.currentTimeMillis()-j2_d_start)); }
結果:
JdkSerializationRedisSerializer序列化時間:8ms,序列化后的長度:1325
JdkSerializationRedisSerializer反序列化時間:4
GenericJackson2JsonRedisSerializer序列化時間:52ms,序列化后的長度:17425
GenericJackson2JsonRedisSerializer反序列化時間:60
Jackson2JsonRedisSerializer序列化時間:4ms,序列化后的長度:9801
Jackson2JsonRedisSerializer反序列化時間:4
2、性能總結
- JdkSerializationRedisSerializer序列化后長度最小,Jackson2JsonRedisSerializer效率最高。
- 如果綜合考慮效率和可讀性,犧牲部分空間,推薦key使用StringRedisSerializer,保持的key簡明易讀;value可以使用Jackson2JsonRedisSerializer
- 如果空間比較敏感,效率要求不高,推薦key使用StringRedisSerializer,保持的key簡明易讀;value可以使用JdkSerializationRedisSerializer
3、方案一、考慮效率和可讀性,犧牲部分空間
package com.example.demo.config.redisConfig; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig { @Bean(name = "redisTemplate") public RedisTemplate<String, Object> getRedisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>(); redisTemplate.setConnectionFactory(factory); redisTemplate.setKeySerializer(new StringRedisSerializer()); // key的序列化類型 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // value的序列化類型 return redisTemplate; } }
注: new Jackson2JsonRedisSerializer(Object.class)需要指明類型,例如:new Jackson2JsonRedisSerializer(User.class),否則會報錯:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.example.demo.bean.User。
或者開啟默認類型:
ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
這種方式存儲時會自動帶上類的全路徑,占用部分空間:
4、方案二、空間敏感,忽略可讀性和效率影響
@Configuration public class RedisConfig { @Bean(name = "redisTemplate") public RedisTemplate<String, Object> getRedisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>(); redisTemplate.setConnectionFactory(factory); redisTemplate.setKeySerializer(new StringRedisSerializer()); // key的序列化類型 redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); // value的序列化類型 return redisTemplate; } }
注:該方式,對象需要實現接口:Serializable
5、使用示例
@RunWith(SpringRunner.class) @SpringBootTest @WebAppConfiguration public class RedisTest { @Resource private RedisTemplate redisTemplate; @Test public void testRedis1(){ User user = new User(); user.setAge(11); user.setName("我是小王1"); redisTemplate.opsForValue().set("user37",user); System.out.println(redisTemplate.getValueSerializer()); System.out.println(redisTemplate.getKeySerializer()); User result = (User) redisTemplate.opsForValue().get("user37"); System.out.println(result); } }