这里我们不在详细的介绍redis的相关信息,redis是一个非关系型数据库。redis是基于内存进行存储的,存储速度非常快,是关系型数据库的几倍到几十倍,可以1s内完成10万次的读写,适用于查询情况多的网站,用于提升性能。除次之外,redis还提供了简单的事务机制,保证高并发场景下的数据的一致性。
redis的数据结构有字符串,列表,散列(hash),集合,有序集合,基数和地理位置。这里我们主要讨论前五种数据结构。在引入redis的时候我们通常是使用jedis来进行对其的操作,因此需要排除lettuce,导入redis。
- pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xiyou</groupId>
<artifactId>spring-redis</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.13.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>
</project>
spring和redis的整合
spring提供了RedisConnectionFactory接口。该接口可以生成一个RedisConnection接口对象,而RedisConnection接口对象是对Redis底层接口的封装。
(1) RedisConnectionFactory:
该接口是一个工厂,主要是用来配置Redis的连接池,可以设定其最大的连接数,超时时间等属性
(2)RedisConnection:
是一个接口,该接口是redis的连接池,用来生成redis对象
(3)AbstractRedisConnection:
是一个抽象类,实现了RedisConnection
(4)JedisConnection:
实体类,集成子AbstractRedisConnection,用来封装Jedis的相关操作
(5)RedisClusterConnection:
接口,实现了RedisConnection
(6)JedisClusterConnection:
实体类,实现了RedisClusterConnection,用来对集群的支持
(7)StringRedisConnection:
接口,实现了RedisConnection
(8)DefaultStringRedisConnection:
实体类,实现了StringRedisConnection,是对字符串的支持
代码如下:
下面的代码不止生成了一个Redis的工厂(RedisConnectionFactory),还提供了redisTemplate。
RedisTemplate是一个强大的类,他会自己从RedisConnectionFactory中获取连接,然后执行对应的Redis命令,在最后还会自动关闭redis的连接,这些都是redisTemplate的自动封装的。
package com.xiyou.redis.utils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import redis.clients.jedis.JedisPoolConfig;
/**
* reidis的配置类,负责生成connectionFactory
*/
@Configuration
public class RedisConfig {
// 存取redisConnectionFactory
private RedisConnectionFactory connectionFactory;
/**
* 创建redisConnectionFactory的对象
* @return
*/
@Bean("redisConnectionFactory")
public RedisConnectionFactory initRedisConnectionFactory(){
// connectionFactory只用一个对象就行,所以如果该对象有值,不再继续执行
if(connectionFactory != null){
return connectionFactory;
}
// 创建配置类对象
JedisPoolConfig poolConfig = new JedisPoolConfig();
// 最大的空闲数
poolConfig.setMaxIdle(30);
// 最大的连接数
poolConfig.setMaxTotal(50);
// 最大的等待毫秒数
poolConfig.setMaxWaitMillis(2000);
// 创建Jedis的工厂
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(poolConfig);
jedisConnectionFactory.setHostName("127.0.0.1");
jedisConnectionFactory.setPort(6379);
connectionFactory = jedisConnectionFactory;
return connectionFactory;
}
/**
* 生成redisTemplate对象,用来操作redis
* @return
*/
@Bean("redisTemplate")
public RedisTemplate<Object, Object> initRedisTemplate(){
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
redisTemplate.setConnectionFactory(initRedisConnectionFactory());
return redisTemplate;
}
}
package com.xiyou.redis.test;
import com.xiyou.redis.utils.RedisConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
/**
* redis的测试
*/
public class RedisTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(RedisConfig.class);
RedisTemplate<Object, Object> redisTemplate = applicationContext.getBean(RedisTemplate.class);
// 操作string类型
redisTemplate.opsForValue().set("key1", "value1");
redisTemplate.opsForHash().put("hash", "field", "hvalue");
}
}
通过上面的结果我们可以看到redis已经保存成功,但是他的结果保存出来我们并不认识,这是为什么呢??
其原因就是Redis是一种基于字符串进行存储的NoSQL,而Java是基于对象的语言,对象是无法存储到redis中的,不过java提供了序列化的机制,只需要实现Serializable接口,这就代表了类的对象可以进行序列化,序列化后我们就可以用redis对其进行存储。但是我们通常看不懂java序列化后的结果。
具体如何修改呢?请往下看,我来揭晓
可以看到我们上述的操作有两个步骤操作redis,一个是字符串,一个是hash操作,此时我们看打印,发现分别打开了两个链接,操作了一个redis,关闭连接,然后再一次开启,继续操作再关闭,那么这种操作就有点不舒服了,浪费了资源,如果我们想在一个redis连接下操作应该如何做呢?继续看。
-
解决redis的存储序列化问题:
- spring关于Redis的序列化器设置
一般我们默认的序列化器是JdkSerializationRedisSerializer,就是说如果我们没有显示的指出序列化器的时候,我们就会用该序列化器,我们之前的redis存储的看不懂的就是这个序列化器序列化的,我们通常使用的是StringRedisSerializer序列化器。
通常序列化器有两个方法:
(1)serialize
序列化方法,将对象变为字符串
(2)deserialize
反序列化,将字符串转为对象
其原理图:
-
redisTemplate的序列化器属性
redisTemplate会自动初始化StringRedisSerializer,所以我们若想获取该序列化器可以直接获取
RedisSerializer stringRedisSerializer = redisTemplate.getStringSerializer();
获取成功后,我们需要给其设置相关属性
属性 | 描述 | 备注 |
---|---|---|
defaultSerializer | 默认的序列化器 | 如果没有设置,则默认使用JdkSerializationRedisSerializer |
keySerializer | Redis的键序列化器 | 如果没有设置,则使用默认序列化器 |
valueSerializer | Redis的值序列化器 | 如果没有设置,则使用默认序列化器 |
hashKeySerializer | Redis散列结构的field序列化器 | 如果没有设置,则使用默认序列化器 |
hashValueSerializer | Redis散列结构的value序列化器 | 如果没有设置,则使用默认序列化器 |
stringSerializer | 字符串序列化器 | RedisTemplate自动赋值为StringRedisSerializer对象 |
- 修改redis的配置
在生成redisTemplate的时候指明序列化器
/**
* 生成redisTemplate对象,用来操作redis
* @return
*/
@Bean("redisTemplate")
public RedisTemplate<Object, Object> initRedisTemplate(){
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
redisTemplate.setConnectionFactory(initRedisConnectionFactory());
// 设置字符串序列化器,让存储在redis中的代码我们可以看明白
// 1. 得到字符串序列化器,将其解析成字符串的形式
// **redisTemplate会自动初始化StringRedisSeriaizer,所以这里可以直接获取**
RedisSerializer stringRedisSerializer = redisTemplate.getStringSerializer();
// 设置key的序列化器
redisTemplate.setKeySerializer(stringRedisSerializer);
// 设置value的序列化器
redisTemplate.setValueSerializer(stringRedisSerializer);
// 设置hash的key的序列化器
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// 设置hash的value的序列化器
redisTemplate.setHashValueSerializer(stringRedisSerializer);
return redisTemplate;
}
其他代码不做改变,结果如下
我们redis的两个赋值操作都不是在一个redis的连接中完成的,这样是在浪费资源,Spring为我们提供了RedisCallback和SessionCallback两个接口,他们的作用是让RedisTemplate进行回调,通过他们可以在同一条连接下执行多个Redis命令。
其中sessionCallback提供了良好的封装,对于开发者比较友好,因此在实际的开发中应该优先使用;RedisCallback接口比较底层,需要处理的内容比较多,可读性较差,所以非必要的时候尽量不要选择他。
- 修改测试类的代码。新增方法
/**
* 使用sessionCallback转换规则,较为简单 推荐使用
* @param redisTemplate
*/
public void useSessionCallback(RedisTemplate redisTemplate){
redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations redisOperations) throws DataAccessException {
redisOperations.opsForValue().set("keyCall", "valueCall");
redisOperations.opsForHash().put("hashCall", "fieldCall", "valueCall");
return null;
}
});
}
/**
* 使用redisCallBack转换规则,较为负责,尽量不要使用,可以改写底层
* @param redisTemplate
*/
public void useRedisCallBack(RedisTemplate redisTemplate){
redisTemplate.execute(new RedisCallback() {
@Override
public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
redisConnection.set("redisKey".getBytes(), "valueRedis".getBytes());
redisConnection.hSet("hashRedis".getBytes(), "fieldRedis".getBytes(), "hRedis".getBytes());
return null;
}
});
}
- 运行结果
通过上面的结果可以看到,我们操作了两条redis的记录,只是开启了一个redis连接。操作完成后,redis自动关闭了连接