redis的缓存问题

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

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

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

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

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

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

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

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

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

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

  1. 缓存的更新策略

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

定期生成

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

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

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

完成统计热词的过程

根据热词,找到搜索结果的数据

把得到的数据同步到部署缓存中间件的服务器上

控制这些缓存服务器自动重启

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

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

实时生成

用户在每次查询的时候,系统集群就会经历以下的过程:

首先在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 默认策略,当内存不足以容纳新写入数据时,新写入操作会报错.

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

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失效,导致了缓存命中率陡然下降,让数据库的压力陡然上升,甚至发生宕机.

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

Redis服务器宕机,或者是Redis的集群大量服务器宕机

短时间之内,设置了很多过期时间相同的key.过期的时候也正好是同一时间过期.

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

加强监控报警,保证Redis集群的可用性.

我们可以选择不给Redis的数据设置过期时间或者是设置过期时间的时候,加入一些随机的因子(避免同一时间过期).

3.4 缓存击穿

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


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/2301_80050796/article/details/143659308

相关推荐
PGCCC8 分钟前
【PGCCC】Postgresql 缓存替换算法
数据库·缓存·postgresql
盖盖衍上18 分钟前
4. SQL视图
数据库·sql
枫哥和java2 小时前
python serializer, model drf通过序列化器, 模型获取mysql 一张表某个字段数据库现存的最大值
数据库·python·mysql
敲敲敲-敲代码3 小时前
【SQL实验】索引操作(菜单操作和命令操作)
数据库·sql·sqlserver
谦谦均3 小时前
深入解析PostgreSQL中的PL/pgSQL语法
数据库·postgresql
夜色呦4 小时前
Spring Boot实验室管理系统:高效科研管理解决方案
数据库·spring boot·php
敲敲敲-敲代码5 小时前
【SQL实验】视图操作(菜单操作和命令操作)
数据库·sql·sqlserver
2401_857026236 小时前
Spring Boot技术在实验室信息管理中的应用
数据库·spring boot·php
"追风者"6 小时前
mysql数据库(六)pymysql、视图、触发器、存储过程、函数、流程控制、数据库连接池
数据库·mysql
guokanglun6 小时前
数据库视图
数据库