[Redis] Redis缓存机制

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343

🏵️热门专栏:

🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482

🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482

🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482

🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482

🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482

🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482

🎃Redis(97平均质量分)https://blog.csdn.net/2301_80050796/category_12777129.html?spm=1001.2014.3001.5482

🐰RabbitMQ(97平均质量分) https://blog.csdn.net/2301_80050796/category_12792900.html?spm=1001.2014.3001.5482

感谢点赞与关注~~~

目录

  • [1. 为何要使用Redis作为缓存](#1. 为何要使用Redis作为缓存)
  • [2. 缓存的更新策略](#2. 缓存的更新策略)
  • [3. 缓存预热,缓存穿透,缓存雪崩,缓存击穿(常考面试题)](#3. 缓存预热,缓存穿透,缓存雪崩,缓存击穿(常考面试题))
    • [3.1 缓存预热](#3.1 缓存预热)
    • [3.2 缓存穿透](#3.2 缓存穿透)
    • [3.3 缓存雪崩](#3.3 缓存雪崩)
    • [3.4 缓存击穿](#3.4 缓存击穿)

1. 为何要使用Redis作为缓存

我们知道,Redis最主要的用途主要有三个方面,首先就是存储数据,也就是把它当做一种内存数据库,其次是缓存,这是Redis最常用的一种场景,然后就是消息队列,不过我们一般不使用Redis作为消息队列,市面上有很多现成的消息队列中间件,比Redis好用的多.

我们为什么要使用Redis作为缓存呢?

首先我们需要知道在计算机中,访问数据的速度CPU寄存器>内存>硬盘>网络,速度快的设备可以作为速度慢的设备的缓存.我们最常见的就是内存作为硬盘的缓存(这也是Redis的定位).当然我们前面也曾经谈到过,硬盘也可以作为网络的缓存.当访问到某个网站上的数据的时候,浏览器就会从服务器上获取数据并进行展示,如果某些数据的体积较大,变化频率又不是很高的时候,就可以通过网络直接存储数据到本地硬盘,当下一次打开页面的时候,就不必从通过网络从服务器上再次获取数据了 .

缓存中存储的数据,一般都是我们经常需要访问的数据 ,这些数据虽然不多,但是却可以满足绝大部分的查询情况,满足的是二八定律,20%的数据可以应对80%的请求.

我们通常使用Redis作为数据库的缓存(MySQL),但是我们知道,数据库的性能并不是很高:

  • 首先数据库是把数据存储在硬盘上的,硬盘的读写速度并不快,尤其是随机访问.

  • 其次如果查询不能命中索引的话,就需要对表进行遍历,这就会大大增加硬盘读写的次数.

  • 如果是一些复杂的查询,比如联合查询,就需要对表进行笛卡尔积的操作,效率更低.

就是因为MySQL这种关系型数据库效率比较低,所以可以承担的并发量比较有限,一旦请求多了,数据库的压力就会很大,甚至发生宕机 .如果想要解决MySQL并发量的问题,我们有两种解决的策略,一种是开源,就是引入更多的机器,构成数据库集群,另一种就是节流,即引入缓存,这就是典型的方案,把一些频繁读取的热点数据,保存到缓存上,后续在查询数据的时候,如果缓存中已经存在了,就可以不再访问MySQL了.Redis也就相当于一个护盾一样,把MySQL给罩住了.

2. 缓存的更新策略

如何知道Redis中存储的是哪些数据呢,换句话说,如何知道哪些数据是热点数据呢?这就涉及到了缓存的更新策略.缓存的更新策略有如下两种:

  1. 定期生成
    顾名思义,就是每隔一定的周期就会挑选出热点数据(比如一天/一周/一个月),Redis会把访问的数据,使用日志的形式来记录下来.此处的热点数据,就可以根据当前这里的统计维度(日志),来定期进行更新.

比如搜索引擎: 在搜索引擎中,所搜索的查询词就是要访问的数据,这时候,系统就会统计搜索的查询词,把这些查询词统计到日志之中.之后就可以对这些日志进行统计了.统计这一天/一周/一个月每个词出现的频率,在根据频率降序排列,取出比如前20%的词作为热点数据,接下来就可以把这些热点词涉及到的搜索结果,提前拎出来,就可以放到类似于Redis这样的缓存中了.

缓存数据总体的流程大体如下:

  • 完成统计热词的过程
  • 根据热词,找到搜索结果的数据
  • 把得到的数据同步到部署缓存中间件的服务器上
  • 控制这些缓存服务器自动重启

以上定期缓存策略的优点就是实现起来比较简单,过程更加可控,方便排查问题.

缺点也很明显,就是实时性不够,如果出现一些突发性事件,有一些本来不是热词的内容就会突然成为热词,这些数据就会都打到数据库上,新的热词可能会给数据库带来较大的压力.

  1. 实时生成
    用户在每次查询的时候,系统集群就会经历以下的过程:
  • 首先在Redis中查询要查询的数据,如果数据在Redis中存在的话,会直接返回结果.
  • 如果Redis中不存在的话,就会从数据库中查询,之后把查询到的结果同时也写入Redis中.
    在这种模式之下,经过一段时间的动态平衡,Redis中的key就会逐渐都成为了热点数据.但是如果这样一直不停地写Redis的话,就会达到内存上限(这里的上限不一定是服务器的内存上限,也可能是Redis中的配置项参数,最多可以使用多少内存空间 ,我们可以在conf文件中通过maxmemory配置项来配置).
  • 为了解决上述的问题,Redis就引入了=="内存淘汰策略[常见面试题]"==.Redis中的内存淘汰策略主要有以下的几种:
    • FIFO(first in first out) 先进先出
      把缓存中存在时间最久的(先进来的数据)数据淘汰掉.
    • LRU(least recently used) 淘汰最近未使用的
      记录每个key的最近访问时间,把最近访问的时间最早的key淘汰掉.
    • LFU(least frequently used) 淘汰访问次数最少的
      记录每个key最近一段时间的访问次数,把访问次数最少的淘汰掉.
    • Random 随机淘汰
      随机抽取一名幸运儿淘汰掉

举例说明:甄嬛传

我们需要把Redis想想成一个皇帝.把数据想象成后宫佳丽.

FIFO:皇后肯定是最先受宠的,但是现在已经年老色衰了,皇后失宠

LRU:统计最近宠幸的时间,宠幸时间最早的佳丽失宠

LFU:统计最近宠幸的次数,宠幸次数最少的佳丽失宠

Random:随机挑选一个佳丽失宠

上面的策略,具体要采用哪种,就要具体场景具体分析了.在Redis的配置文件中,我们有一个配置项,就可以配置内存淘汰策略.这里的淘汰策略,我们可以自己实现,当然Redis也内置了一些淘汰策略供我们使用.

• volatile-lru 当内存不足以容纳新写入数据时,从设置了过期时间的key中使用LRU(最近最少使用)算法进行淘汰

• allkeys-lru 当内存不足以容纳新写入数据时,从所有key中使用LRU(最近最少使用)算法进行淘汰.

• volatile-lfu 4.0版本新增,当内存不足以容纳新写入数据时,在过期的key中,使用LFU算法进行删除key.

• allkeys-lfu 4.0版本新增,当内存不足以容纳新写入数据时,从所有key中使用LFU算法进行淘汰.

• volatile-random 当内存不足以容纳新写入数据时,从设置了过期时间的key中,随机淘汰数据.

• allkeys-random 当内存不足以容纳新写入数据时,从所有key中随机淘汰数据.

• volatile-ttl 在设置了过期时间的key中,根据过期时间进行淘汰,越早过期的优先被淘汰.(相当于FIFO,只不过是局限于过期的key)

• noeviction 默认策略,当内存不足以容纳新写入数据时,新写入操作会报错.

3. 缓存预热,缓存穿透,缓存雪崩,缓存击穿(常考面试题)

3.1 缓存预热

我们上面提到,缓存中更新数据有两种策略,一种是定期生成,一种是实时生成.定期生成不涉及"预热",但是实时生成会涉及到.

Redis服务器在首次接入之后,服务器里是没有任何数据的,此时,所有的请求就都会答到数据库上,如果请求以下子太多,很容易导致数据库服务器宕机.缓存预热就是用来解决上述的问题的,首先通过离线的方式,通过一些统计途径,先把热点数据找到一批,导入到Redis中,此时导入的这批热点数据,就可以帮mysql承担很大的压力了.随着时间的推移,逐渐就会使用新的热点数据淘汰掉旧的热点数据.

3.2 缓存穿透

我们在Redis中查询某个key,在Redis中没有,在mysql中也没有,这个key肯定也不会被更新到Redis中.这次查询没有,下次查还没有,如果像这样的数据存在很多,并且还反复查询,一样会给mysql带来很大的压力 .

为什么会有上面的这种情况呢?有以下的几种情况:

  • 业务涉及不合理,比如缺少相关参数的校验,导致非法的key也被查询了.
  • 开发/运维人员误删数据库上的数据.
  • 黑客恶意攻击

那么如何解决上述内存穿透的问题呢?

  • 首先针对第一种场景,我们需要改进业务逻辑,加强监控报警机制,但是这也是一种亡羊补牢的一种方式.更加靠谱的方案,如果发现这个key在Redis中和mysql中都不存在,我们仍然把这个值写入到Redis中,value设置为一个非法的值(比如""),后续在访问非法数据的时候,就会返回设置好的非法的数据.当然我们还可以进入布隆过滤器.每次查询Redis/mysql之前,都判定一下key是否在布隆过滤器上存在.
  • 之后就是第二种场景,我们就需要开发/运维人员介入,尽快回复数据.
  • 第三种场景,需要安全团队的介入.

3.3 缓存雪崩

由于在短时间内,Redis上大规模的key失效,导致了缓存命中率陡然下降,让数据库的压力陡然上升,甚至发生宕机.

造成上述这种问题的一般有两种情况:

  1. Redis服务器宕机,或者是Redis的集群大量服务器宕机
  2. 短时间之内,设置了很多过期时间相同的key.过期的时候也正好是同一时间过期.

解决上述问题的主要方式是:

  1. 加强监控报警,保证Redis集群的可用性.
  2. 我们可以选择不给Redis的数据设置过期时间或者是设置过期时间的时候,加入一些随机的因子(避免同一时间过期).

3.4 缓存击穿

缓存击穿相当于缓存雪崩的一种特殊情况,雪崩是大量的key发生了过期,但是穿透与雪崩的区别就是==只是热点数据突然发生了过期,==这就会使得大量的请求打在了数据库上,导致数据库发生宕机.相比雪崩,击穿过期的是热点数据,访问频率更高,影响更大.

解决方案有以下几种:

  1. 基于统计的方式发现热点key,并设置为永不过期.但是这种往往需要服务器的结构做出较大的调整
  2. 进行必要的服务降级,例如使用分布式锁,限制同时请求数据库的并发数.我们接下来介绍
相关推荐
liuxin3344556633 分钟前
高效编程训练:Spring Boot系统设计与实践
数据库·spring boot·php
地图之家家长2 小时前
19.(开发工具篇mysql库)mysql锁表问题解决
数据库·mysql
秋意钟4 小时前
Java注解
数据库
看山还是山,看水还是。5 小时前
Oracle的字符串函数
运维·数据库·安全·oracle
东方巴黎~Sunsiny5 小时前
⚙️ 如何调整重试策略以适应不同的业务需求?
java·数据库·kafka
daopuyun5 小时前
全面介绍软件安全测试分类,安全测试方法、安全防护技术、安全测试流程
网络·数据库·安全·安全测试
綦枫Maple6 小时前
Jmeter基础篇(23)TPS和QPS的异同
数据库·jmeter·oracle
ac.char6 小时前
在 Ubuntu 上安装 Redis 并为其设置登录密码
linux·redis·ubuntu
川石课堂软件测试6 小时前
性能测试|JMeter接口与性能测试项目
javascript·数据库·python·jmeter·单元测试