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

相关推荐
月光水岸New2 小时前
Ubuntu 中建的mysql数据库使用Navicat for MySQL连接不上
数据库·mysql·ubuntu
狄加山6752 小时前
数据库基础1
数据库
我爱松子鱼2 小时前
mysql之规则优化器RBO
数据库·mysql
chengooooooo2 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
Rverdoser3 小时前
【SQL】多表查询案例
数据库·sql
Galeoto3 小时前
how to export a table in sqlite, and import into another
数据库·sqlite
希忘auto3 小时前
详解Redis在Centos上的安装
redis·centos
人间打气筒(Ada)3 小时前
MySQL主从架构
服务器·数据库·mysql
leegong231114 小时前
学习PostgreSQL专家认证
数据库·学习·postgresql
喝醉酒的小白4 小时前
PostgreSQL:更新字段慢
数据库·postgresql