redis
1、NoSQL(Not Only SQL)
NoSQL,Not Only SQL(不仅仅时SQL),泛指非关系型数据库。
1、关系型数据库
使用标准的sql语言进行操作。
采用关系模型来组织数据的数据库,关系模型就是二维表格模型。一张二维 表的表名就是关系,二维表中的一行就是一条记录,二维表中的一列就是一个字段。
优点:容易理解;使用方便,通用的 sql 语言;易于维护,丰富的完整性(实体完整性、参照完整性和用户定义的完整性) 大大降低了数据冗余和数据不一致的概率缺点。
缺点:磁盘 I/O 是并发的瓶颈;海量数据查询效率低;横向扩展困难,无法简单的通过添加硬件和服务节点来扩展性能和负载能力, 当需要对数据库进行升级和扩展时,需要停机维护和数据迁移;多表的关联查询以及复杂的数据分析类型的复杂 sql 查询,性能欠佳。因为要保证 ACID
2、非关系型数据库
没有固定的关系,以键值对的形式储存。
非关系型,分布式,一般不保证遵循ACID原则的数据存储系统。键值对存储, 结构不固定。
优点:结构简单易扩展高性能;灵活的数据模型。
缺点:只适合存储一些较为简单的数据;不适合复杂查询的数据;不适合持久存储海量数据。
2、Redis概述
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的,使用 C 语言编写的,支持网络交互的,内存中的 Key-Value 数据结构存储系统,支持多种语言,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如字符串(strings), 散列(hashes), 列 表(lists), 集合(sets), 有序集合(sorted sets)。
redis主要用来做数据缓存,让数据距离程序更近,redis运行在内存中,数据也保存在内存中,一般把redis称为中间件。
特点:Redis是键值对,存储在内存中,也可以存储在硬盘上。存储结构简单,性能高,易于扩展,支持多语言,保证操作原子性,支持多种数据类型。
3、Redis安装配置
1、安装准备
检查 gcc 版本: gcc -v
Redis6以上版本要求gcc必须在5.3以上
如果不能满足需求,升级 gcc,步骤如下:
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
scl enable devtoolset-9 bash
需要注意的是scl 命令启用只是临时的,退出 shell 或重启就会恢复原系统 gcc 版本。
如果要长期使用 gcc 9.3 的话执行下面命令。
echo -e “\nsource /opt/rh/devtoolset-9/enable” >>/etc/profile
如果已经安装 gcc,版本满足要求,跳过上面升级步骤. 如果没有安装 gcc,安装步骤:
dnf group install “Development Tools”
dnf install man-pages
2、安装
下载:https://redis.io/download
cd /usr/local/src/ 在线下载 或者 在官网下载
wget https://download.redis.io/releases/redis-6.0.8.tar.gz
解压: tar -zxvf redis-6.0.8.tar.gz
cd redis-6.0.8
预编译: make
创建文件: mkdir -p /usr/local/redis
安装到指定目录: make install PREFIX=/usr/local/redis
进入安装文件 bin 目录:cd /usr/local/redis/bin
./redis-server 启动服务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X6C5mgBa-1662022809630)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660102506396.png)]
3、基本设置
1、从redis的源码目录中复制redis.conf到redis的安装目录:cp redis.conf /opt/redis/bin/
2、修改redis.conf文件:vim redis.conf
第一,后台运行:daemonize no 改为 daemonize yes。
第二,设置密码:requirepass 你的密码。
第三,注释绑定的 id,默认只能本机访问,这样就可可以远程访问了。
3、启动命令: ./redis-server redis.conf
4、查看 Redis 运行状态: ps -ef | grep redis
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pD335Tw9-1662022809631)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660124602743.png)]
5、进入客户端模式: ./redis-cli
6、输入密码:auth 密码
7、测试:ping
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sepViQfh-1662022809632)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660124578902.png)]
8、ctrl+c 退出客户端模式
9、停止redis服务:./redis-cli shutdown
4、redis数据类型
redis有5种基本常用类型:string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(sorted set:有序集合)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jQGrRdwT-1662022809632)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660127651575.png)]
底层数据结构有6种,分别是:简单动态字符串、双向链表、压缩列表、哈希表、跳表和整数数组。其对应关系如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VOI86f41-1662022809632)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660127790922.png)]
全局hash表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bVslsBLu-1662022809633)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660127993255.png)]
hash可以在O(1)的时间内计算出hash值并且找到对应的entry位置,entry里面是一个key指针和value指针,还有其他信息,这是redis高性能的原因之一。
但凡碰见hash表的,就一定会有哈希冲突(碰撞)问题,并且哈希冲突问题不可避免。
可以通过链式哈希解决冲突,即在同一个桶里面的元素使用链表保存。但是当链表过长就会导致查找性能变差,redis为了快,使用了两个全局哈希表,用于rehash操作,增加现有的哈希桶数量,减少哈希冲突。
开始默认使用hash表1保存键值对数据,hash表2此刻没有分配空间,当数据越来越多的触发rehash操作时,则执行下列操作:
1、给hash表2分配更大的空间。
2、给hash表1的数据重新映射拷贝到hash2中。
3、释放hash表1的空间。
将 hash 表 1 的数据重新映射到 hash 表 2 的过程并不是一次性 的,这样会造成 redis 阻塞,无法提供服务。而是采用了渐进式 rehash,这样每次处理客户端请求的时候,先从hash 表1中第一个索引开始,将这个位置的所有数据拷贝到hash表2中,就这样将rehash分散到多次请求过程中,避免耗时阻塞。
string(字符串)
string是redis最基本的类型,一个key对应一个value。string类型是二进制安全的,所以redis的string可以包含任何数据,比如jpg图片或者序列化的对象。string类型的值最大能存储512MB。
单值缓存
set key value
get key
del key
对象缓存(不对对象中数据操作时可以使用)
set user:1 value(json 格式数据)
计数器
set new_views:1 0 设置文章访问量
incr news_views:1 文章访问量+1
decr news_views:1 文章访问量-1
get news_views:1 获得值
Web集群session共享
session + redis 实现session共享
hash(哈希)
redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象(对对象中的数据发生修改情况)。 存的是字符串和字符串值之间的映射,比如要存储用户购物车等信息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZU94qC3e-1662022809633)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660180858826.png)]
hset key field value 存储一个哈希表 key 的键值
hmset key field value [field value …] 存储多个键值对
hget key field 获取哈希表 key 对应的 field 键值
hmget key field [field …] 批量获取哈希表 key 中多个 field 键值
hdel key field [field …] 删除哈希表 key 中的 field 键值
hlen key 返回哈希表 key 中的 field 的数量
hgetall key 返回哈希表 key 中所有的键值
hincrby key field 增加的值(减少给负数)
电商购物车
1)以用户 id 为 key
2)商品 id 为 field
3)商品数量为 value
购物车操作
1)添加商品→hset cart:1001 10088 1
2)增加数量→hincrby cart:1001 10088 1
3)商品总数→hlen cart:1001
4)删除商品→hdel cart:1001 10088
5)获取购物车所有商品→hgetall cart:1001
list(列表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
lpush key value[value…] 将一个或多个值插入到 key 列表的表头(最左边)
rpush key value[value…] 将一个或多个值插入到 key 列表的表尾(最右边)
lpop key 移除并返回 key 列表的头元素
rpop key 移除并返回 key 列表的尾元素
lrange key start stop 返回列表key中指定区间内的元素,区间以偏移量start和 stop
常见用法
Stack= LPUSH + LPOP =FILO
Queue= LPUSH + RPOP
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5T1Ablbo-1662022809633)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660182297963.png)]
使用场景:可以用来存储接收到的消息数据。
set(集合)
Redis 的 Set 是无序集合。
sadd key member[member…] 往集合 key 中存入元素,元素存在则忽略,
若 key 不存在则新建
srem key member[member…] 从集合 key 中删除元素
smembers key 获取集合 key 中所有元素
scard key 获取集合 key 的元素个数
zset(sorted set:有序集合)
redis zset 也是不允许重复的成员,但是是有序的。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。zset 的成员是唯一的,但分数(score)却可以重复。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-as8cd041-1662022809634)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660182409757.png)]
zadd key score member[[score member]…] 往有序集合 key 中加入带分值元素
zrem key member[member…] 从有序集合 key 中删除元素
zscore key member 返回有序集合 key 中元素 member 的分值
zincrby key increment member 为有序集合 key 中元素 member 的分值加上 increment
zcard key 返回有序集合 key 中元素个数
zrange key start stop[withscores] 正序获取有序集合 keyastart 下标到 stop下标的元素
使用场景:记录微信朋友圈记录点赞用户。
5、设置失效时间
有时候我们并不希望 redis 的 key 一直存在。例如缓存,验证码等数据,我们希望它们能在一定时间内自动的被销毁。redis 提供了一些命令,能够让我们对 key设置过期时间,并且让 key 过期之后被自动删除。
设置值时直接设置有效时间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lIqLS1S6-1662022809634)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660187614977.png)]
EX 表示以秒为单位;PX 表示以毫秒为单位。EX,PX 不区分大小写set name jim EX 30 设置失效时间为 30 秒;
ttl 键 查看剩余时间(秒)
pttl 键 查看剩余时间(毫秒)
设置值后设置有效时间
expire 键 时间(秒)
pexpire 键 时间(毫秒)
6、springBoot 集成使用 redis
Spring-data-redis 是 spring 大家族的一部分,提供了在 srping 应用中通过简单的配置访问 redis 服务,对 reids 底层开发包(Jedis, JRedis,and RJC)进行了高度封装,RedisTemplate 提供了 redis 各种操作。
spring-data-redis 针对 jedis 提供了如下功能:
1、连接池自动管理,提供了一个高度封装的“RedisTemplate”类。
2、针对 jedis 客户端中大量 api 进行了归类封装,将同一类型操作封装为operation 接口。
ValueOperations:简单 K-V 操作
SetOperations:set 类型数据操作
ZSetOperations:zset 类型数据操作
HashOperations:针对 map 类型的数据操作
ListOperations:针对 list 类型的数据操作
3、将事务操作封装,有容器控制。
4、针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)JdkSerializationRedisSerializer:POJO 对象的存取场景,使用 JDK 本身序列化机制.
StringRedisSerializer:Key 或者 value 为字符串的场景,根据指定的charset 对数据的字节序列编码成 string,是“new String(bytes, charset)”和“string.getBytes(charset)”的直接封装。是最轻量级和高效的策略。
JacksonJsonRedisSerializer:jackson-json 工具提供了 javabean 与 json 之 间的转换能力,可以将 pojo 实例序列化成 json 格式存储在 redis 中,也可以将json 格式的数据转换成 pojo 实例。
搭建
1、添加 redis 依赖
<!--添加 redis 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、配置连接redis
redis:
host: 192.168.244.128
port: 6379
password: 100
database: 0
pool:
max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 8 # 连接池中的最大空闲连接
min-idle: 0 # 连接池中的最小空闲连接
timeout: 5000ms # 连接超时时间(毫秒)
3、注入RedisTemplate
@Autowired
RedisTemplate redisTemplate;
4、测试
import com.ffyc.backserver.model.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.util.concurrent.TimeUnit;
//import org.springframework.data.redis.core.RedisTemplate;
//import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
//import org.springframework.data.redis.serializer.StringRedisSerializer;
@SpringBootTest
class BackServerApplicationTests {
@Autowired
User user;
@Autowired
RedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("name", "aa");
redisTemplate.opsForValue().set("users", users, 10 * 1000, TimeUnit.MILLISECONDS);
redisTemplate.hasKey("name");
redisTemplate.opsForValue().get("name");
redisTemplate.delete("users");
//序列化键值
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<User>(User.class));
}
}
需要被 Redis 缓存的类,必须实现序列化接口。
7、redis线程模型
1、redis是单线程模式还是多线程模式
2、为什么设计为单线程模型速度也很快
因为redis是基于内存的操作,查找和操作的时间复杂度都是O(1),所以CPU并不是redis的瓶颈,redis的瓶颈可能是机器内存或网络带宽的大小。
第一,redis是基于内存操作的,它的所有数据在内存中,因此所有的运行都是内存级别的,所以它的性能高。
第二,数据结构简单,redis的数据结构是为了自身专门量身打造的,这些数据结构的查找和操作的时间复杂度都是O(1)。
第三,多路复用和非阻塞I/O,redis使用I/O多路复用功能来监听多个socket连接客户端,这样就可以使用一个线程来处理多个情况,从而减少线程切换带来的开销,同时也避免了I/O阻塞操作,大大提高了redis的性能。
第四,避免上下文切换,因为是单线程模式,因此避免了不必要的上下文切换和多线程竞争,省去了多线程切换带来的时间和性能上的开销,而且单线程不会导致死锁的问题发生。
8、redis持久化
redis是一个内存数据库,数据保存在内存中,但是内存的数据变化是很快的,也容易发生丢失。redis提供了持久化机制,分别是RDB(Redis DataBase)和AOF(Append Only File)。
RDB方式
RDB持久化是在指定的时间间隔内将内存中的数据集快照写入磁盘,也是默认的持久化方式,这种方式就是将内存中数据以快照的方式写入到二进制文件中,默认文件名为dump.rdb。
触发快照的时机
第一,save的规则满足的情况下,会触发rdb规则。save:这里是用来配置触发redis的RDB持久化条件,表示m秒内数据集存在n次修改时,自动触发bgsave。
save 900 1:表示900秒内至少1个键被更改则进行快照。
save 300 10:表示300秒内至少10个键被更改则进行快照。
save 60 10000:表示60秒内至少10000个键被更改则进行快照。
如果不需要持久化,那么可以注释所有的save行来停用保存功能。
第二,执行flushall命令,也会触发我们的rdb规则。
第三,退出redis,也会产生一个rdb文件,备份就自动生成一个dump.rdb,关机后下次启动redis会自动将文件中的数据还原到内存中。
RDB方式持久化适合大规模的数据恢复,对数据的完整性要求不高。RDB持久化需要一定的时间间隔进程操作,如果redis意外宕机了,这个最后一次修改的数据就没有了。
AOF方式
以日志的形式来记录每个写操作,将redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据。也就是redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
开启方式
修改redis.conf配置文件,开启AOF机制
appendonly no #默认是不开启AOF模式的,改为yes开启。
appendfilename appendonly.aof #默认文件名是appendonly.aof,可以通过appendfilename参数修改
AOF同步机制
appendfsyncc always #每次修改都会sync,消耗性能。
appendfsync everysec #每秒执行一次sync,可能会丢失这1s的数据(默认)
9、redis事务
redis事务本质是一组命令的集合,一个事务中所有命令都会被序列化,在事务执行过程中,会按照顺序执行。redis事务没有隔离级别的概念。所有的命令在事务中,并没有直接被执行,只有发起执行exec命令时才会被执行。
redis单条命令式保持原子性的,但是事务不保证原子性。
redis事务:开启事务(multi)、命令入队(…)、执行事务(exec)、放弃事务(discard)。
10、主从复制
主从复制是将一台redis服务器的数据复制到其他redis服务器,前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到从节点。
使用一台redis实例作为主机,其余的作为备份机。主机和备份机的数据完全一致,主机支持数据的写入和读取,从机只支持与主机数据同步和读取。客户端可以将数据写入到主机,由主机自动将数据的写入操作同步到从机。主从模式很好的解决了数据备份问题,并且由于主从服务数据几乎完全是一致的,因而可以将写入数据的命令发送给主机执行,而读取数据的命令发送给不同的从机执行,从而达到读写分离的目的。
主从复制的作用主要包括:
第一,数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
第二,故障恢复:当主节点出现问题时,可以由节点提供服务,实现快速的故障恢复,实际上时一种服务的冗余。
第三,负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务,分担服务器负载。尤其是在写少读多的场景下,可以通过多个从节点分担读负载,可以大大提高redis服务器的并发量。
第四,高可用(集群)基石:主从复制还是哨兵和集群能够实施的基础。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tW6DLcrI-1662022809634)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660276867684.png)]
主从复制配置
主从复制只需要配置从库即可。打开redis客户端登录,使用info replication查看。主从复制可以搭建真集群,也可以搭建伪集群。真集群就是有多台主机,每台主机安装一个redis。伪集群就是在一台主机上复制多份配置,修改端口,运行多个redis实例。
主从配置实例
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3pm0HW9L-1662022809635)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660277976847.png)]
主机配置:
bind 0.0.0.0 #任何ip都可以访问
daemonize yes #后台运行
pidfile /var/run/redis_6379.pid #进程号文件
logfile “6379.log” #日志文件,注意:修改文件名只是为了区分
dbfilename dump6379.rdb #数据文件
requirepass root #主机密码
从机配置:
#bind 注释
daemonize yes #后台运行
pidfile /var/run/redis_6380.pid #进程号文件
logfile “6380.log” #日志文件 ,注意:修改文件名只是为了区分
dbfilename dump6380.rdb #数据文件
replicaof 主机 ip 主机端口
masterauth 主机密码
从机进入客户端模式 ./redis-cli -p 从机端口
进入客户端,使用 info replication 命令查看模式
主机
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M4PfKrNW-1662022809636)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660278624079.png)]
从机
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JcsPk47r-1662022809636)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660278663464.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TUhS2d2u-1662022809636)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660278720424.png)]
测试主写从读
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6GnZYGbh-1662022809637)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660278695528.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9gotFtrN-1662022809637)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660278742139.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6NTo1Xqu-1662022809637)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660278758200.png)]
11、哨兵机制
哨兵模式是一种特殊的模式,首先redis提供了哨兵的命令,哨兵是一个独立的进程,可以独立运行。原理是通过哨兵发送命令,等待redis服务器响应,从而监控运行多个redis实例。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eq0F5OTc-1662022809638)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660293130684.png)]
哨兵集群
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n2eURLtN-1662022809638)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660293798902.png)]
12、Key过期策略
1、立即处理。在设置键的过期,创建一个回调事件,当过期时间达到时,由时间处理器自动执行键的删除操作。立即删除能保证内存中数据的最大新鲜度,因为它保证键值会在过期后马上被删除,所占用的内存也会被释放,但是立即删除对cpu是最不友好的,因为删除操作会占用cpu时间。
2、惰性删除。惰性删除是指某个键值过期后不会马上被删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。所以惰性删除的缺点很明显:浪费内存,dict字典和expires字典都要保存这个键值的信息。
3、定时删除。每隔一段时间,对expires字典进行检查,删除里面的过期键。通过限制删除操作执行的时长和频率,来减少删除操作对cpu的影响,另一方面定时删除也2有效的减少了因惰性删除带来的内存浪费。
总结:第二种为被动删除,第一种和第三种为主动删除,且第一种实时性更高。redis使用的过期键值删除策略是:惰性删除+定期删除。
13、缓存穿透、缓存击穿、缓存雪崩
缓存处理流程
前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到就更新缓存,并返回结果,如果数据库也没有取到,就返回空结果。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZDrNcfQG-1662022809638)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660296942862.png)]
缓存穿透
key对应的数据在数据库中并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据库,从而可能压垮数据库。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能会压垮数据库。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iMBZlA5W-1662022809639)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660314355286.png)]
解决办法:1、将这个空对象设置到缓存里面去,下次请求的时候就可以从缓存里面获取,一般会将空对象设置一个较短的过期时间。
2、对参数进行校验,不合法参数进行拦截。
缓存击穿
某个key对应的数据库中存在,但在redis中的某个时间节点过期了,此时若有大量并发请求发过来,这些请求发现缓存过期,都会从后端DB加载数据并回设到缓存,这个时候大量并发的请求可能会瞬间把后端DB压垮。
解决办法:1、热点数据设置永不过期。
2、加锁:上面的现象是多个线程同时去查询数据库这条数据,那么我们可以在第一个查询数据的请求上使用一个互斥锁来锁住它,其它的线程走到这一步拿不到锁就等着,等第一个线程查询到数据,然后将数据放到redis缓存起来。后面的线程进来发现已经有缓存了,就直接走缓存。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LJ88dckR-1662022809639)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660356268537.png)]
缓存雪崩
缓存雪崩是指在高并发情况下,大量的缓存失效或者缓存层出现故障,于是所有的请求都会到达数据库,数据库的调用量会暴增,造成数据库挂掉的情况。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qEBx6rlc-1662022809640)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1660356766232.png)]
解决方法:1、随机设置key失效时间,避免大量key集体失效。
setRedis(Key,value,time + Math.random() * 10000);
2、若是集群部署,可将热点数据均匀分布在不同的redis库中也能避免key全部失效的问题。
3、不设置过期时间。
4、跑定时任务,在缓存失效前刷新缓存。
总结
缓存穿透是redis中不存在缓存key,缓存击穿是redis某一个热点key突然失效,缓存雪崩是大面积的key缓存时效,最终受害者都是数据库。
对于redis宕机,请求全部走数据库的情况,我们这样解决:
宕机前:实现redis的高可用(主从架构+Sentinel(哨兵)),尽量避免redis挂掉的这种情况。
宕机中:万一redis真的挂了,我们可以设置本地缓存(ehcache)+限流,尽量避免数据库也被挂掉(保证服务正常运行)。
宕机后:redis持久化,重启后自动从磁盘加载数据,快速恢复缓存数据。