redis是一款高性能key-value(键值对)内存型数据库,是非关系型数据库的一种,它采用单线程的架构方式,避免了多线程存在的锁处理造成的资源耗费,读取速度非常快,非常适合变化不是太大但数据量很大的数据的存储和读取。
redis中的数据类型:String、list、hash、zset、set五种。
项目中涉及到数据查询的操作,先从redis中查询,若redis中不存在则从数据库中查询,再更新到redis中,以后再次执行相同的查询则直接从redis中查询,提高了效率。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
2、application.properties中配置redis相关的参数
##配置redis
spring.redis.database=0
spring.redis.host=119.3.250.240
spring.redis.password=888888
spring.redis.port=6379
3、使用redis
redis中有两个模板类:RedisTemplate和StringRedisTemplate。通过模板类可以对redis数据库进行增删改查的操作,redis数据库中存放数据是要对key、value进行序列化的,RedisTemplate模板类默认采用JdkSerializationRedisSerializer序列化类对key和value进行序列化;而StringRedisTemplate默认采用StringRedisSerializer序列化类对key和value进行序列化。
上面说了这么多理论有啥用?对项目有什么影响?如何在项目中使用呢?别急,咱们下面就结合代码进行说明。
1)如果采用StringRedisTemplate模板,首先看它的下源码:
/**
从源码中可以看到,StringRedisTemplate模板类默认采用StringRedisSerializer序列化类对
key和value进行序列化,通过该模板类传参时,key和value要求是String类型。
*/
public class StringRedisTemplate extends RedisTemplate<String, String> {
public StringRedisTemplate() {
this.setKeySerializer(RedisSerializer.string());
this.setValueSerializer(RedisSerializer.string());
this.setHashKeySerializer(RedisSerializer.string());
this.setHashValueSerializer(RedisSerializer.string());
}
public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
this();
this.setConnectionFactory(connectionFactory);
this.afterPropertiesSet();
}
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
}
}
下面用StringRedisTemplate做个小测试:
@SpringBootTest
class DemoApplicationTests {
//使用StringRedisTemplate模板
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
public void junitTest() {
//往redis中存入key、value键值对
stringRedisTemplate.opsForValue().set("webAddress","www.54gwz.cn");
//从redis中取出key对应的value值
System.out.println("---webAddress-->"+stringRedisTemplate.opsForValue().get("webAddress"));
}
}
//单元测试日志截取如下:
2020-06-17 11:21:39.891 INFO 77817 --- [ main] io.lettuce.core.EpollProvider : Starting without optional epoll library
2020-06-17 11:21:39.894 INFO 77817 --- [ main] io.lettuce.core.KqueueProvider : Starting without optional kqueue library
----------------webAddress----------->www.54gwz.cn
redis客户端查询结果如下:
119.3.250.240:6379> keys *
1) "webAddress"
119.3.250.240:6379> get webAddress
"www.54gwz.cn"
2)如果采用RedisTemplate模板,首先看下它的源码(部分截取):
通过源码分析得到key、value、hashKey、hashValue均默认采用JdkSerializationRedisSerializer方式进行了序列化。
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
private boolean initialized = false;
private boolean enableDefaultSerializer = true;
@Nullable
private RedisSerializer<?> defaultSerializer;
@Nullable
private RedisSerializer keySerializer = null;
@Nullable
private RedisSerializer valueSerializer = null;
@Nullable
private RedisSerializer hashKeySerializer = null;
@Nullable
private RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = RedisSerializer.string();
public RedisTemplate() {
}
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
//defaultSerializer默认序列化类为JdkSerializationRedisSerializer
if (this.defaultSerializer == null) {
this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
}
/**key、value、hashKey、hashValue均默认采用JdkSerializationRedisSerializer
的方式进行了序列化
*/
if (this.enableDefaultSerializer) {
if (this.keySerializer == null) {
this.keySerializer = this.defaultSerializer;
defaultUsed = true;
}
if (this.valueSerializer == null) {
this.valueSerializer = this.defaultSerializer;
defaultUsed = true;
}
if (this.hashKeySerializer == null) {
this.hashKeySerializer = this.defaultSerializer;
defaultUsed = true;
}
if (this.hashValueSerializer == null) {
this.hashValueSerializer = this.defaultSerializer;
defaultUsed = true;
}
}
if (this.enableDefaultSerializer && defaultUsed) {
Assert.notNull(this.defaultSerializer, "default serializer null and not all serializers initialized");
}
if (this.scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor(this);
}
this.initialized = true;
}
下面我们用这种默认的JdkSerializationRedisSerializer序列化的方式进行一个小测验:
@SpringBootTest
class DemoApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void junitTest() {
redisTemplate.opsForValue().set("username","wangxinli");
System.out.println("---username-->"+redisTemplate.opsForValue().get("username"));
}
}
执行单元测试打印日志结果:(结果正确!)
2020-06-17 17:57:40.440 INFO 1373 --- [main] io.lettuce.core.EpollProvider: Starting without optional epoll library
----------------username----------->wangxinli
此时在redis客户端进行查询:发现username前面会有一连串特殊字符,原因在于key值进行了JdkSerializationRedisSerializer序列化,如果想去掉key值的特殊字符,需要对key值指定序列化方式。
119.3.250.240:6379> keys *
1) "\xac\xed\x00\x05t\x00\busername"
2) "webAddress"
自定义序列化方式:
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//指定key、hashKey的序列化方式为StringRedisSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//指定value、hashValue的序列化方式为JdkSerializationRedisSerializer
redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
//redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
再次执行看下效果:
@SpringBootTest
class DemoApplicationTests {
/**此处自动装配的RedisTemplate对象的序列化方式就是上面自定义的,即(key和hashKey
采用StringRedisSerializer,value和hashValue采用的
JdkSerializationRedisSerializer)
*/
@Autowired
private RedisTemplate redisTemplate;
@Test
public void junitTest() {
redisTemplate.opsForValue().set("usernameSerialize","wangxinliSerialize");
System.out.println("---usernameSerialize-->"+redisTemplate.opsForValue().get("usernameSerialize"));
}
}
执行单元测试,看下打印的日志效果:(没有问题)
2020-06-17 18:27:24.330 INFO 1738 --- [main] io.lettuce.core.EpollProvider
2020-06-17 18:27:24.331 INFO 1738 --- [main].....省略
---usernameSerialize-->wangxinliSerialize
再看下redis客户端:发现usernameSerialize前面的特殊字符消失了,我们的更改序列化方式
生效啦,我们成功啦!!!!
119.3.250.240:6379> keys *
1) "usernameSerialize"
2) "\xac\xed\x00\x05t\x00\busername"
119.3.250.240:6379> get usernameSerialize
"\xac\xed\x00\x05t\x00\x12wangxinliSerialize"
但别高兴的太早,有没有发现一个新的问题呢,usernameSerialize对应的value值也变成有特殊字符啦!!!!别担心,这是正常现象,因为value值也是采用的JdkSerializationRedisSerializer进行序列化的鸭!我们也可以采用上述指定序列化的方式进行更改。