Redis缓存击穿

Redis

非关系数据库,基于内存存储,存储方式是k-v

端口号默认是6379

缓存使用原则

什么时候,什么样的数据能够保存在Redis中

  1. 数据量不能太大
  2. 保存的数据一般是常用且不会是数据库中频繁修改的数据
  3. 使用的越频繁,Redis保存这个数据越值得

缓存淘汰策略

前提:Redis将数据保存在内存中,内存的容量是有限的,如果Redis服务器的内存已经全满,现在还需要向Redis中保存新的数据,返回默认错误:noeviction ,如何操作才能避免

就是需要缓存淘汰策略

  • allkeys-random:从所有的数据中随机删除
  • volatile-random:有过期时间的数据中随机删除
  • volatile-ttl: 删除剩余有效时间最少的数据
  • allkeys-lru:所有数据中删除上次使用时间最久的数据
  • volatile-lru:有过期时间的数据中删除上次使用时间最久的数据
  • allkeys-lfu:所有数据中删除使用频率最少的数据
  • volatile-lfu:有过期时间的数据中删除使用频率最少的数据

缓存穿透

数据在redis和数据库中都不存在,这就叫穿透

如果这个请求反复出现,就会反复连接数据库,严重的导致数据库性能降低甚至宕机

解决方法:业界主流的是布隆过滤器

布隆过滤器的使用步骤:

  1. 针对现有所有数据,生成布隆过滤器
  2. 在业务逻层中,判断Redis之前先检查这个id是否在布隆过滤器中
  3. 如果布隆过滤器判断这个id不存在,直接返回
  4. 如果布隆过滤器判断这个id存在,再进行后面业务的执行

缓存击穿

数据在数据库中存在,Redis中没有,需要从数据库中查询再存储到Redis,这种情况就叫击穿

因为缓存时数据会设置有效期,所以少量击穿不是异常情况

缓存雪崩

同一时间出现大量击穿,就可能造成雪崩

出现这种情况的原因是:同一时间大量数据失效

避免雪崩很简单,只需要将失效时间设置成不一样的即可

Redis持久化

Redis的特征是基于内存存储

但是内存的特性是读写速度快,但是不安全,一旦宕机数据就会从内存中丢失,就需要从数据库重新查询所有数据库,这个是很慢的,更有可能,Redis本身是有新数据的,还没有和数据库同步就宕机了

所以Redis支持了持久化,会将数据保存在本地硬盘上

Redis持久化策略有两种:RDB、AOF

RDB

全称Redis Database Backup,本质上是数据库快照(就是当前Redis中所有数据转换成二进制的对象,保存在硬盘上)

默认情况下,会生成一个dump.rdb的文件

当Redis宕机或断电,需要恢复数据时,可以恢复成dump.rdb文件中生成的所有内容

我们可以通过以下配置开启RDB

save 60 5

配置效果:1分钟内如果有5个key以上被修改,就启动rdb数据库快照程序

优点:

​ 因为是整体Redis数据的二进制格式,数据恢复是整体恢复

缺点:

​ 生成的rdb文件是一个硬盘上的文件,读写效率是较低的

​ 如果突然断电,只能恢复最后一次生成的rdb中的数据

AOF

全称是Append Only File,AOF策略是将Redis运行过的所有命令(日志)备份下来

这样即使Redis断电,我们也可以根据运行过的日志,恢复为断电前的样子

配置信息,如下

appendonly yes

就可以保存运行过的指令的日志了

理论上任何运行过的指令都可以恢复,但是实际情况下,Redis非常繁忙,我们会将日志命令缓存之后,整体发送给备份,减少io次数以提高备份的性能和对Redis性能的影响

实际开发中,配置一般会采用每秒将日志文件发送一次的策略,断电最多丢失1秒数据

优点:

​ 相对RDB来讲,信息丢失的较少

缺点:

​ 因为保存的是运行的日志,所以占用空间较大(Redis的AOF为减少日志的大小,支持AOF rewrite,简单来说就是将日志中无效的语句删除,能够减少占用的空间)

实际开发中RDB和AOF是可以同时开启的

也可以单个开启

Redis存储原理

Redis将内存划分为16384个区域

将数据的Key使用CRC16算法计算出一个值。取余16384,余数0~16383

将这个key保存在计算结果对应的槽位,再次查询这个key时,那么直接到这个槽位查找,效率很高

Redis集群

Redis最小运行状态是一台服务器

这个服务器的运行状态直接决定Redis是否可用

如果它离线了,整个项目无Redis可用,那么就会面临崩溃

为了防止这种情况发生,我们可以准备一台备用机

主从复制

image-20230213214803842
也就是主机(master)工作时,安排一台备用机(slave)实时同步数据,万一主机宕机,我们可以切换到备用机运行

这种方案缺点是:如果master一直可用,salve没有任何实质性的作用

读写分离

image-20230213215416336
这样slave在master正常工作时,也能分担Master的工作

但是如果master宕机,实际上主备机的切换,还是需要人工介入,这还是需要时间的

那么想要做到故障自动切换,可以使用哨兵模式实现

哨兵模式

image-20230215194554167
哨兵模式可以实现故障自动切换

哨兵节点每隔固定时间向所有节点发送请求,如果正常响应认为该节点正常;如果没有响应,认为该节点出现问题,哨兵能自动切换主备机

如果主机master下线,自动切换到备用机

但是这样的模式存在问题
image-20230215195038701
但是如果哨兵判断节点状态时,发送了误判,那么就会错误将master下线,降低整体运行性能

哨兵集群

image-20230215195456381
将哨兵做成集群,由多个哨兵投票决定是否下线某一个节点

哨兵集群中,每个节点都会定时向master和slave发送请求

如果请求有集群半数以上哨兵节点没有收到正常响应,会认为该节点下线

分片集群

当业务不断扩展,并发不断增高

只有一个节点支持写操作无法满足整体性能的要求时,系统性能就会达到瓶颈

这个时候我们就要部署多个支持写操作的节点,进行分片,来提供程序的整体性能

分片就是每个节点负责不同的区域

Redis有0~16383个槽

准备三台写操作的节点,分别是MasterA、MasterB、MasterC

负责的槽位,可能是,以下的分配方式

MasterA 负责 0~5000

MasterB 负责5001~10000

MasterC 负责10001~16383

一个key根据CRC16算法只能得到固定的结果,一定在指定的服务器上找到数据
image-20230215200951949
有了这个集群结构,我们就可以更加稳定和更加高效的处理业务请求

为了节省哨兵服务器的成本,我们也可以在redis集群中直接添加哨兵功能,即master/slave节点完成数据读写的任务同时也会互相检测他们的状态