第7章 集成Redis缓存

版权声明:我已委托“维权骑士”(rightknights.com)为我的文章进行维权行动 https://blog.csdn.net/huangwenyi1010/article/details/82821328

7.1 Redis缓存介绍

7.1.1 Redis概述

Redis是一个基于内存的,单线程高性能key-value型数据库,读写性能优异。和Memcached缓存相比,Redis支持丰富的数据类型,包括string(字符串)、list(链表)、set(集合)、zset(sorted set有序集合)和hash(哈希类型)。因此Redis在企业中被广泛使用。

7.1.2 Redis服务器安装

Redis项目本身是不支持Window,但是Microsoft开放技术小组开发和维护这个Windows端口针对Win64。所以我们可以在网络上下载Redis的Window版本。具体步骤如下:
打开官网http://redis.io/,点击Download,具体如图7-1所示。
在这里插入图片描述
图7-1 Redis下载首页
在弹出的页面中,找到Learn more选项,并点击进入,具体如图7-2所示。

在这里插入图片描述
图7-2 点击Learn more链接
在弹出的页面中选择【releases】选项,具体如图7-3所示。

在这里插入图片描述

图7-3 选择Download ZIP下载Redis

在弹出的界面中选择Redis3.0.504这个版本,选择其它版本也可以,点击【Redis-x64-3.0.504.zip】下载Redis安装包,如图7-4所示。

在这里插入图片描述
图7-4 下载Redis 3.0.504安装包
解压下载的安装包【Redis-x64-3.0.504.zip】,双击【redis-server.exe】,Redis服务就运行起来了,如图7-5所示。同时我们可以看到Redis启动成功的界面,如图7-6所示。

在这里插入图片描述
图7-5 启动redis服务器

在这里插入图片描述
图7-6 redis启动成功界面

7.1.3 Redis缓存测试

Redis安装成功之后,我们可以在安装包里找到Redis客户端程序redis-cli.exe,如图7-7所示,双击redis-cli.exe,打开Redis客户端界面,如果7-8所示。

在这里插入图片描述
图7-7 启动redis客户端 图7-8 redis启动成功界面

下面就使用Redis客户端对Redis的几种数据类型做基本的增删改查操作练习,具体代码如下:
字符串类型的增删改查:

###增加一个值key为name,value为ay
127.0.0.1:6379> set name 'ay'
OK
###查询name的值
127.0.0.1:6379> get name
"ay"
###更新name的值为al
127.0.0.1:6379> set name 'al'
OK
###查询name的值
127.0.0.1:6379> get name
"al"
###删除name的值
127.0.0.1:6379> del name
(integer) 1
###查询是否存在name,0代表不存在
127.0.0.1:6379> exists name
(integer) 0
127.0.0.1:6379>
List集合的增删改查:
###添加key为user_list,value为’ay’,’al’的list集合
127.0.0.1:6379> lpush user_list 'ay' 'al'
(integer) 2
###查询key为user_list的集合
127.0.0.1:6379> lrange user_list 0 -1
1) "al"
2) "ay"
###往list尾部添加love元素
127.0.0.1:6379> rpush user_list 'love'
(integer) 3
###往list头部添加hope元素
127.0.0.1:6379> lpush user_list 'hope'
(integer) 4
###查询key为user_list的集合
127.0.0.1:6379> lrange user_list 0 -1
1) "hope"
2) "al"
3) "ay"
4) "love"
###更新index为0的值
127.0.0.1:6379> lset user_list 0 'wish'
OK
###查询key为user_list的集合
127.0.0.1:6379> lrange user_list 0 -1
1) "wish"
2) "al"
3) "ay"
4) "love"
###删除index为0的值
127.0.0.1:6379> lrem user_list 0 'wish'
(integer) 1
###查询key为user_list的集合
127.0.0.1:6379> lrange user_list 0 -1
1) "al"
2) "ay"
3) "love"
127.0.0.1:6379>

Set集合的增删改查:

###添加key为user_set,value为"ay" "al"  "love"的集合
127.0.0.1:6379> sadd user_set "ay" "al"  "love"
(integer) 3
###查询key为user_set集合
127.0.0.1:6379> smembers user_set
1) "al"
2) "ay"
3) "love"
###删除value为love,返回1表示删除成功,0表示失败
127.0.0.1:6379> srem user_set 'love'
(integer) 1
###查询set集合所有值
127.0.0.1:6379> smembers user_set
1) "al"
2) "ay"
###添加love元素,set集合是没有顺序的,所以无法判断添加到那个位置
127.0.0.1:6379> sadd user_set 'love'
(integer) 1
###查询set集合所有值,发现添加到第二个位置
127.0.0.1:6379> smembers user_set
1) "al"
2) "love"
3) "ay"
###添加love元素,由于set集合已经存在,返回0代表添加不成功,但是不会报错
127.0.0.1:6379> sadd user_set 'love'
(integer) 0

Hash集合的增删改查:

###清除数据库
127.0.0.1:6379> flushdb
OK
###创建hash,key为user_hset,字段为user1,值为ay
127.0.0.1:6379> hset user_hset "user1"  "ay"
(integer) 1
###往key为user_hset添加字段为user2,值为al
127.0.0.1:6379> hset user_hset "user2"  "al"
(integer) 1
###查询user_hset字段长度
127.0.0.1:6379> hlen user_hset
(integer) 2
###查询user_hset所有字段
127.0.0.1:6379> hkeys user_hset
1) "user1"
2) "user2"
###查询user_hset所有值
127.0.0.1:6379> hvals user_hset
1) "ay"
2) "al"
###查询字段user1的值
127.0.0.1:6379> hget user_hset "user1"
"ay"
###获取key为user_hset所有的字段和值
127.0.0.1:6379> hgetall user_hset
1) "user1"
2) "ay"
3) "user2"
4) "al"
###更新字段user1的值为new_ay
127.0.0.1:6379> hset user_hset "user1" "new_ay"
(integer) 0
###更新字段user2的值为new_al
127.0.0.1:6379> hset user_hset "user2" "new_al"
(integer) 0
###获取key为user_hset所有的字段和值
127.0.0.1:6379> hgetall user_hset
1) "user1"
2) "new_ay"
3) "user2"
4) "new_al"
###删除字段user1和值
127.0.0.1:6379> hdel user_hset user1
(integer) 1
###获取key为user_hset所有的字段和值
127.0.0.1:6379> hgetall user_hset
1) "user2"
2) "new_al"
127.0.0.1:6379>

SortedSet集合的增删改查

###清除数据库
127.0.0.1:6379> flushdb
OK
###SortedSet集合添加ay元素,分数为1
127.0.0.1:6379> zadd user_zset  1 "ay"
(integer) 1
###SortedSet集合添加al元素,分数为2
127.0.0.1:6379> zadd user_zset  2 "al"
(integer) 1
###SortedSet集合添加love元素,分数为3
127.0.0.1:6379> zadd user_zset  3 "love"
(integer) 1
###按照分数由小到大查询user_zset集合元素
127.0.0.1:6379> zrange user_zset 0 -1
1) "ay"
2) "al"
3) "love"
###按照分数由大到小查询user_zset集合元素
127.0.0.1:6379> zrevrange user_zset 0 -1
1) "love"
2) "al"
3) "ay"
###查询元素ay的分数值
127.0.0.1:6379> zscore user_zset "ay"
"1"
###查询元素love的分数值
127.0.0.1:6379> zscore user_zset "love"
"3"

7.2 Spring Boot集成Redis缓存

7.2.1 Spring Boot缓存支持

在Spring Boot中提供了强大的基于注解的缓存支持,可以通过注解配置方式,低侵入的给原有Spring应用增加缓存功能,提高数据访问性能。Spring Boot为我们配置了多个CacheManager的实现,我们可以根据具体的项目要求使用相应的缓存技术。具体如图7-9所示。

在这里插入图片描述
图7-9 Spring Boot缓存配置类

从图7-9可知,Spring Boot支持许多类型的缓存,比如:EhCache、JCache、Redis等。在不添加任何额外配置的情况下,Spring Boot默认使用SimpleCacheConfiguration,考虑到Redis缓存在企业中被广泛使用,故选择用Redis缓存来进行讲解。

7.2.2 引入依赖

在Spring Boot中集成Redis,首先需要在pom.xml文件中引入所需的依赖,具体代码如下:

<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

7.2.3 添加缓存配置

在pom文件引入Redis所需的依赖之后,我们需要在application.properties文件中添加如下的配置信息:

### redis缓存配置
### 默认redis数据库为db0
spring.redis.database=0
### 服务器地址,默认为localhost
spring.redis.host=localhost
### 链接端口,默认为6379
spring.redis.port=6379
### redis密码默认为空
spring.redis.password=

7.2.4 测试用例开发

在application.properties配置文件中添加完Redis配置之后,我们在测试类MySpringBootApplicationTests.java继续添加如下的代码:

@Resource
	private RedisTemplate redisTemplate;

	@Resource
	private StringRedisTemplate stringRedisTemplate;

	@Test
	public void testRedis(){
		//增 key:name,value:ay
		redisTemplate.opsForValue().set("name","ay");
		String name = (String)redisTemplate.opsForValue().get("name");
		System.out.println(name);
		//删除
		redisTemplate.delete("name");
        //更新
		redisTemplate.opsForValue().set("name","al");
		//查询
		name = stringRedisTemplate.opsForValue().get("name");
		System.out.println(name);
	}

RedisTemplate和StringRedisTemplate都是Spring Data Redis为我们提供的两个模板类用来对数据进行操作,其中StringRedisTemplate只针对键值都是字符串的数据进行操作。在应用启动的时候,Spring会为我们初始化这两个模板类,通过@Resource注解注入即可使用。

RedisTemplate和StringRedisTemplate除了提供opsForValue方法用来操作简单属性数据之外,还提供了以下其它主要数据访问方法:
1)opsForList:操作含有list的数据。
2)opsForSet:操作含有set的数据。
3)opsForZSet:操作含有ZSet(有序set)的数据。
4)opsForHash:操作含有hash的数据。

当我们的数据存放到Redis的时候,键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用JdkSerializationRedisSerializer,而StringRedisTemplate默认使用StringRedisSerializer。

7.2.5 测试

7.2.4节测试用例代码开发完成之后,我们运行单元测试用例,除了可以在控制台查看打印结果信息和在redis客户端查看数据之外,我们还可以使用Redis Client客户端工具查看Redis缓存数据库中的数据。Redis Client客户端软件,大家可以到网络上下载,并安装到自己的操作系统中。 安装完成之后,可以看到如图7-10所示的界面。

在这里插入图片描述
图7-10 redis client界面

在图7-10中,我们可以看到Redis默认有16个数据库,客户端与Redis建立连接后会自动选择0号数据库。通过该客户端,我们可以清楚的查看Redis数据库中存放的数据情况,同时可以在客户端中对数据进行增删改查等操作,方便使用。

7.3 Redis缓存在Spring Boot中使用

7.3.1 监听器Listener开发

在6.2.2节当中,我们已经简单的开发好AyUserListener监听器类,并在上下文启动时候打印信息。在这节当中,我们想在上下文初始化的方法中,加载数据库中的所有用户数据,并存放到Redis缓存中。之所以要把用户数据存放到缓存中,是因为用户的数据属于变动不大的数据,适合存放到缓存中,在应用需要获取用户数据时,可以直接到Redis缓存中获取,不用到数据库中获取数据库连接查询数据,提高数据的访问速度。具体代码如下:

/**
 * 描述:监听器
 * @author Ay
 * @date   2017/11/4
 */
@WebListener
public class AyUserListener implements ServletContextListener {

    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private AyUserService ayUserService;
    private static final String ALL_USER = "ALL_USER_LIST";

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //查询数据库所有的用户
        List<AyUser> ayUserList =  ayUserService.findAll();
        //清除缓存中的用户数据
        redisTemplate.delete(ALL_USER);
        //将数据存放到redis缓存中
redisTemplate.opsForList().leftPushAll(ALL_USER, ayUserList);
        //真实项目中需要注释掉,查询所有的用户数据
        List<AyUser> queryUserList = redisTemplate.opsForList().range(ALL_USER, 0, -1);
        System.out.println("缓存中目前的用户数有:" + queryUserList.size() + " 人");
        System.out.println("ServletContext上下文初始化");    
}

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext上下文销毁");
    }
}

redisTemplate.opsForList().leftPushAll:查询缓存中所有的用户数据, ALL_USER键若不存在,会创建该键及与其关联的List,之后再将参数中的ayUserList从左到右依次插入。
redisTemplate.opsForList().range:取链表中的全部元素,其中0表示第一个元素,-1表示最后一个元素。
在7.2.3节中,我们已经提到,当我们的数据存放到Redis的时候,键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用JdkSerializationRedisSerializer,而StringRedisTemplate默认使用StringRedisSerializer。所有我们需要让用户类AyUser(/src/main/java/com.example.demo.model)实现序列化接口Serializable,具体代码如下:

/**
 * 描述:用户表
 * @Author 阿毅
 * @date   2017/10/8.
 */
@Entity
@Table(name = "ay_user")
public class AyUser implements Serializable{
    //省略代码
}

7.3.2 项目启动缓存数据

在7.3.1节中,我们已经开发好AyUserListener监听器类和AyUser用户类,重新启动项目,这时候数据库表ay_user中的所有数据都会加载到Redis缓存中。在contextInitialized方法中打断点调试,出现如图7-9所示的图片,代码数据已经成功被加载到缓存中。 同时,我们也可以用Redis Client客户端软件来查看用户数据是否存放到缓存中。

在这里插入图片描述
图7-9 redis断点调试界面

7.3.3 更新缓存数据

项目启动加载所有用户数据到缓存之后,我们需要修改AyUserServiceImpl中的接口,比如
findById、save、delete等方法。因为如果在Redis缓存中查询不到数据,我们需要到数据库查询,如果能够在数据库中查询到数据,除了返回数据之外,还需要把数据更新到缓存中。这样下次再次查询数据时,就不需要到数据库中查询数据。这里主要对方法findById进行修改AyUserServiceImpl具体需要修改的代码如下:

//省略代码
@Service
public class AyUserServiceImpl implements AyUserService {

    @Resource(name = "ayUserRepository")
    private AyUserRepository ayUserRepository;

    @Resource
    private RedisTemplate redisTemplate;

    private static final String ALL_USER = "ALL_USER_LIST";
    @Override
    public AyUser findById(String id){
        //step.1  查询Redis缓存中的所有数据
        List<AyUser> ayUserList = redisTemplate.opsForList().range(ALL_USER, 0, -1);
        if(ayUserList != null && ayUserList.size() > 0){
            for(AyUser user : ayUserList){
                if (user.getId().equals(id)){
                    return user;
                }
            }
        }
        //step.2  查询数据库中的数据
        AyUser ayUser = ayUserRepository.findOne(id);
        if(ayUser != null){
            //step.3 将数据插入到Redis缓存中
            redisTemplate.opsForList().leftPush(ALL_USER, ayUser);
        }
        return ayUser;
    }
    //省略

}

对于save、delete等方法的修改,思路是一样的,这里就不一一重复累述,作为大家的任务自己实现。虽然引入Redis缓存用户数据可以提高访问性能,但是带来的代码复杂度也是可想而知的。所以在以后的工作钟,在性能和代码复杂度的权衡下,大家要根据具体的业务场景去加以选择,不可滥用缓存。这是要跟大家提醒的重要点。

7.3.4 测试

7.3.3节代码开发完成之后,我们在测试类MySpringBootApplicationTests下继续添加如下的
测试方法:

@Test
	public void testFindById(){
		Long redisUserSize = 0L;
		//查询id = 1 的数据,该数据存在于redis缓存中
		AyUser ayUser = ayUserService.findById("1");
		redisUserSize = redisTemplate.opsForList().size("ALL_USER_LIST");
		System.out.println("目前缓存中的用户数量为:" + redisUserSize);
		System.out.println("--->>> id: " + ayUser.getId() + " name:" + ayUser.getName());
		//查询id = 2 的数据,该数据存在于redis缓存中
		AyUser ayUser1 = ayUserService.findById("2");
		redisUserSize = redisTemplate.opsForList().size("ALL_USER_LIST");
		System.out.println("目前缓存中的用户数量为:" + redisUserSize);
		System.out.println("--->>> id: " + ayUser1.getId() + " name:" + ayUser1.getName());
		//查询id = 4 的数据,不存在于redis缓存中,存在于数据库中,所以会把数据库查询的数据更新到缓存中           
		AyUser ayUser3 = ayUserService.findById("4");
		System.out.println("--->>> id: " + ayUser3.getId() + " name:" + ayUser3.getName());
		redisUserSize = redisTemplate.opsForList().size("ALL_USER_LIST");
		System.out.println("目前缓存中的用户数量为:" + redisUserSize);

	}

代码开发完成之后,我们重新启动项目,数据库中的3条数据,具体如图7-10所示,会重新被添加到Redis缓存中。项目启动成功之后,我们往数据库表ay_user添加id为4的第四条数据,具体如图7-11所示。

在这里插入图片描述
图7-10 数据库钟存在3条数据 图7-11 插入id为4的数据

最后我们执行单元测试方法testFindById(),在Intellij IDEA的控制台中会打印如下的信息:

目前缓存中的用户数量为:3
--->>> id: 1 name:阿毅
目前缓存中的用户数量为:3
--->>> id: 2 name:阿兰
--->>> id: 4 name:test
目前缓存中的用户数量为:4

猜你喜欢

转载自blog.csdn.net/huangwenyi1010/article/details/82821328
今日推荐