SpringBoot+Redis缓存

开发中通常会使用缓存中间件:redis、memcached、ehcache.
redis可以用作数据库、缓存和消息中间件,本文中使用redis作为缓存

一、安装redis

1、docker中安装redis:

sudo docker pull redis

2、启动redis并映射端口

sudo docker run -d -p 6379:6379 --name myredis 镜像名

映射容器服务的 6379 端口到宿主机的 6379 端口。外部可以直接通过宿主机ip:6379 访问到 Redis 的服务

3、windows中安装
在这里插入图片描述
4、连接虚拟机
在这里插入图片描述
右击redis打开控制台console可以输入命令
在这里插入图片描述

二、配置redis

1、pom.xml中引入依赖

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

2、application.properties中配置
springboot中有redis的自动配置类,对使用spring.redis前缀的配置进行自动配置

#连接虚拟机中的redis
spring.redis.host=192.168.0.103
spring.redis.port=6379

3、在测试中注入redis操作的模板:

	@Autowired
	StringRedisTemplate stringRedisTemplate; //模板:操作key-value都是字符串
	
	@Autowired
	RedisTemplate redisTemplate; //模板:操作key-value,都是对象

模板中的方法:

/**
	 * Redis常见的五大数据类型
	 * String(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合)
	 * stringRedisTemplate.opsForValue() [操作String 字符串的]
	 * stringRedisTemplate.opsForList() [List(列表)]
	 * stringRedisTemplate.opsForSet()
	 * stringRedisTemplate.opsForHash()
	 * stringRedisTemplate.opsForZSet()
	 * redisTemplate.opsForValue()  //两个模板使用方法都一样
	 * 
	 */

4、测试用例:
在redis中添加字符串key-value

	@Test
	public void test01() {
		//给redis中保存数据
		stringRedisTemplate.opsForValue().append("msg", "hello");
		//String str=stringRedisTemplate.opsForValue().get("msg");
		//System.out.println(str);
	}

在这里插入图片描述
5、测试例子:利用RedisTemplate类模板的实例保存对象object
注意:保存的对象employee需要是一个可序列化的类,默认如果保存对象,使用jdk序列化机制,序列化后的数据保存到redis中,如下图所示:
在这里插入图片描述

	@Test
	public void test02() {
		//给redis中保存对象
		Employee employee=employeemapper.getEmpById(2);
		redisTemplate.opsForValue().set("emp-01", employee);           //需要一个可序列化的类	
	}
	
	@Test
	void contextLoads() {
		Employee employee=employeemapper.getEmpById(2);
		System.out.println(employee);
	}

在Employee类中继承序列化(Serializable)接口:
在这里插入图片描述
6、改变默认的序列化规则(不使用默认的jdk序列化机制)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//import org.springframework.context.annotation.Primary;
//import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
//import springboot.bean.Department;
import springboot.bean.Employee;
import java.net.UnknownHostException;

@Configuration
public class MyRedisConfig {

    @Bean  //注入模板到容器
    public RedisTemplate<Object, Employee> empRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate<Object, Employee> template = new RedisTemplate<Object, Employee>();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
        template.setDefaultSerializer(ser);
        return template;
    }
}

在测试中注入模板

@Autowired
	RedisTemplate<Object, Employee> empRedisTemplate;

利用该模板保存对象:

@Test
	public void test02() {
		//给redis中保存对象
		Employee employee=employeemapper.getEmpById(2);
		empRedisTemplate.opsForValue().set("emp-01", employee);           //需要一个可序列化的类	
	}

可以直接保存对象
在这里插入图片描述
7、一个固定的自定义RedisTemplate模板
在这里插入图片描述

三、测试缓存

缓存的原理缓存管理器CacheManager–>创建出缓存组件Cache,缓存组件Cache来实际给缓存中CRUD(增删改查)数据
1、引入redis的starter后,容器中保存的是RedisCacheManager
2、RedisCacheManager创建RedisCache作为缓存组件;RedisCache通过操作redis缓存数据
(默认配置类使用SimpleCacheConfiguration,配置完redis后使用的配置类为RedisCacheConfiguration,缓存管理器使用RedisCacheManager)
3、redis中添加缓存,默认保存数据的key-value都是Object,利用序列化保存.如何将保存在缓存中的对象保存为json:
(下图为浏览器中查询2号员工:http://localhost:8080/emp/2后的redis中缓存的数据)

  1. 引入了redis的starter,cacheManager变为RedisCacheManager
  2. 默认创建的RedisCacheManager操作redis的时候使用的是RedisTemplate<Object,Object>
  3. RedisTemplate<Object,Object>是默认使用jdk序列化机制(所以都是乱码)
    在这里插入图片描述

四、Redis持久化

redis是基于内存的数据库,如果不将内存中的数据库状态保存到磁盘中,一旦服务器进程退出,服务器中的数据库状态也将丢失,所以需要持久化功能。

1、RDB

在指定的时间间隔内将内存中的数据集以快照的方式写入磁盘,恢复时将快照文件直接读到内存中。Redis会单独创建一个子进程来进行持久化,将数据写入到临时文件中,待持久化过程结束后用临时文件替换上次持久化好的文件,整个过程中,主进程不进行任何IO操作。如果需要大规模数据恢复,且对数据恢复完整性不是非常敏感,RDB方式比AOF方式更高效,RDB缺点是最后一次持久化后的数据可能丢失。一般情况下默认RDB。
在这里插入图片描述
(1)RDB触发机制:

  1. redis配置文件中save规则满足的情况下,自动触发rdb
  2. 执行flushall命令,触发rdb
  3. 退出redis,产生rdb文件dump.rdb

(2) 恢复rdb文件:
将rdb文件放在redis启动目录下(/usr/local/bin),redis启动时会自动检查dump.rdb恢复数据

(3)优点:

  1. 适合大规模的数据恢复

(4)缺点:

  1. 需要一定的时间间隔进行操作,如果redis意外宕机,最后一次修改的数据丢失
  2. fork进程的时候,会占用一定的内存空间
2、AOF

以日志的形式记录每个写操作,将redis执行过的所有命令都记录下来,只许追加文件但是不可以修改文件,redis启动时会读取该文件重新构建数据,根据日志文件中的内容将写指令全部执行一次,完成数据的恢复。
在这里插入图片描述
默认不开启,在redis配置文件中修改appendonly 为yes
如果aof文件有错误,redis会无法启动,这时候需要修复aof文件,使用redis-check-aof --fix appendonly.aof指令。如果aof文件大于64m,文件太大时,会fork一个新的进程将文件进行重写

(1)优点:

  1. 每一次修改都同步,文件完整性会更好
  2. 每秒同步一次,可能会丢失一秒数据
  3. 可修改从不同步,效率更高

(2)缺点:

  1. 相对于数据文件来说,aof文件远大于rdb文件,修复速度也比rdb慢
  2. aof运行效率也比rdb慢,所以redis默认使用rdb持久化
3、扩展

1、如果同时开启两个持久化方式,当redis重启时会优先载入AOF文件恢复数据,因为AOF数据集比RDB要完整
在这里插入图片描述

五、Redis主从复制

主从复制:将一台服务器的数据复制到其他的redis服务器中,前者为主节点,后者为从节点,数据复制都是单向的,只能由主节点到从节点。master节点以写为主,slave节点以读为主。
1、主从复制的作用

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
  2. 故障恢复:当主节点出现问题时,可以由从节点实现快速的故障恢复
  3. 负载均衡:主从复制配合读写分离,由主节点提供写服务,从节点提供读服务,分担服务器负载,可以大大提高redis服务器的并发量
  4. 高可用(集群):主从复制时哨兵、集群的基础,所以主从复制是redis高可用的基础

2、使用多台redis服务器的原因

  • 单节点的redis服务器会发生单点故障,且一台服务器需要处理所有的请求,负载压力大
  • 单台redis服务器的内存容量有限

3、主从复制配置
只配置从库,不用配置主库(一开始每个redis服务器都是主节点)
只有主库可以写,从库不能写只能读

  1. 复制多份配置文件
  2. 修改每个配置文件的端口
  3. 修改配置文件的pid 名字
  4. 修改配置文件的log文件名字
  5. 修改配置文件中dump.rdb名字
  6. 启动多个redis服务器
  7. 从节点中使用SLAVEOF 主机ip 主机port成为主机的从节点(这只是暂时的配置)
  8. 永久配置主从结构:在配置文件中找到这一行,修改主服务器的ip和端口号在这里插入图片描述

ps:如果某时刻主机宕机了,从节点还是从节点,可以保存之前主节点的所有信息,等主节点恢复后,依然是该从节点的主节点

4、复制原理
slave启动成功连接到master后会发送一个sync命令,master接到命令后,启动后台的存盘进程,同时收集所有接受到的用于修改数据的命令,在后台进程执行完毕之后,master将整个数据文件传送到slave,完成一次完全同步
全量复制:slave服务在接收到数据库文件数据后,将其存盘并加载到内存中
增量复制:master继续将所有收集到的修改命令依次传给slave,完成同步(初次连接master时都会执行)
只要slave重新连接master,将执行一次全量复制,所有的数据都能在从节点中看到

六、哨兵模式

哨兵模式可以监控主机是否出现故障,如果发生故障,根据投票数自动将从节点变成主节点,是自动选举主节点的模式
在这里插入图片描述
1、哨兵的作用:

  1. 通过发送命令,让redis服务器返回监控其的运行状态,包括主服务器和从服务器
  2. 当哨兵检测到master宕机,自动将slave切换成master,然后通过发布订阅模式通知其他从服务器,修改配置文件,他们切换主机

只使用一个哨兵监控会出现问题,因此需要多个哨兵进行监控,各个哨兵之间也形成监控。
在这里插入图片描述
2、哨兵集群执行过程:
当一个哨兵监测到主节点宕机,并不会马上执行重新选举的过程,仅仅是该哨兵的主观判断主机下线,当其他的哨兵也监测到主服务器不可用时,当数量到达一定值时,哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行重新选举(故障转移),切换主节点后,通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这样主机才是客观下线。若之前的主机恢复后,只能变成新主机的从机。

3、优点:

  • 哨兵集群,基于主从复制模式,所有的主从配置优点他都有
  • 主从可以切换,故障可以转移,系统可用性提高
  • 哨兵模式就是主从模式的升级,自动配置

4、缺点:

  • redis不方便在线扩容
  • 哨兵模式配置复杂

七、缓存穿透与雪崩(服务器高可用)

1、缓存穿透(查询不到)

当用户查询一个数据,发现redis缓存中没有,于是会向持久层数据库查询,如果数据库中也没有,本次查询失败,如果用户很多,都会去直接查询数据库,会给数据库带来很大的压力,这就是缓存穿透

解决方法:
(1)使用布隆过滤器,对所有可能查询到的参数以hash形式存储,在控制层先进行校验,不符合则丢弃该请求,从而可以避免对底层存储系统的查询压力
在这里插入图片描述
(2)缓存空对象
当查询数据库失败后,即使是返回的null空对象,也将这个空对象进行缓存,并设置一个过期时间,之后再访问这个数据会从缓存中获取,减少了对数据库的访问。
存在的问题:

  • 可能会缓存很多的空值作为键
  • 即使对空值设置了过期时间,还是会导致缓存和数据库的数据的不一致。
2、缓存击穿(访问量大,缓存过期)

缓存击穿:对于一个热点key,会接受大量的并发访问,当这个key在失效的瞬间,持续的大并发就会穿破缓存层,直接请求数据库。

解决方案:

  • 设置热点数据永不过期
  • 分布式锁:保证对于每个key同时只有一个线程去访问后端服务,其他线程没有获得分布式锁的权限只能等待。
3、缓存雪崩

缓存雪崩:在某一时间段,缓存集中过期失效,redis宕机

解决方案:

  • 增加redis服务器,一台宕机了还有其他的工作
  • 限流降级:缓存失效后,通过加锁或者队列来控制读取数据库写缓存的线程数量,比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
  • 数据预热:在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效时间均匀。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值