Redis缓存击穿、雪崩、穿透!(超详细)

Redis缓存击穿、雪崩、穿透!(超详细)缓存的击穿 穿透和雪崩应该是再熟悉不过的词了 也是面试常问的高频试题 不过 对于这三大缓存的问题 有很多人背过了解决方案 却少有人能把思路给理清的 而且 网络上仍然充斥着 大量不太靠谱的解决方案 难免误人子弟 我的这篇文章 则会对这三大缓存问题 做一个深入的探讨和分析 最有价值的 不是答案本身 而是诞生答案的过程 缓存击穿缓存击穿是什么 大家应该心里都清楚 我只做一个简单通俗的解释

最有价值的,不是答案本身,而是诞生答案的过程。

说白了,就是某个数据,数据库有,但是缓存中没有。

那么,缓存击穿,就会使得因为这一个热点数据,将大量并发请求打击到数据库上,从而导致数据库被打垮。

要解决这个问题之前,我们就先得对整个系统的架构有一定的了解:

在这里插入图片描述
而我们的 redis,在整个系统体系中,作为缓存,就是将很多的请求抗住,过滤掉,
所以最后的数据库的压力就会很小。

那么既然 redis 是作为缓存,

在这里插入图片描述
那么在高并发的情况下,一个 key 过期了,然后,就是几千几万的并发蜂拥而至,这该怎么解决?

首先,大部分人会想到这么一个答案:热点数据永不过期。

在网络上盛行的解决方案有很多:

下面我来详细分析,为什么,这些解决方案,在实际的生产环境中,是无法胜任的。

首先,我来分析,key 永不失效的解决方案,为什么不可行。

但是,你可以说,只存热点数据啊!

但是,什么叫热点数据?你觉得是就是吗?

第二个,网络上很流行的答案就是:加锁

所以实际上,缓存穿透,加锁解决,必须还要涉及到分布式锁的概念。

这里不谈 zookeeper 之类的东西,既然谈 redis,那么就用 redis 来解决这个问题。

在这里插入图片描述
看起来似乎可行,但是,还有问题吗?

我要这么说肯定是有问题,但是,你可以想一想,存在什么问题?

如果你不知道,说明对分布式锁还不够了解,那么,就继续跟着我分析。

现在,我开始假设:

在这里插入图片描述
现在,既然出现了问题,那么,我们一定得想办法去解决。

明显,这么做成本比较高昂,还不如用完善的 zookeeper 去实现分布式锁。

于是,之前的方案,就可以稍作修改:

在这里插入图片描述
看起来很完美。

但是,还有问题吗?

我要这么问了,那么一定说明有。

那么,现在请你先不要拖到后文,先自己思考,会存在什么问题,然后再来看我的分析。

有些人可能就会说,那就放一个原子操作啊!

但是,redis 并没有一个 API,既可以 setnx,又同时给予它一个过期时间。

那该怎么办?

所以,这就需要考验,我们对 redis 的各种机制的掌握程度了。

那么不能回滚的事务也可以用来完成锁操作吗?

这样,就可以保证,redis 不会出现死锁的问题。

这样,解决了死锁问题,就看起来很完美了。

确实,如果只是解决了死锁的问题的情况下,是没有什么问题的。

现在,假设:

所以,锁的超时时间又成了问题。

既然新问题出现了,我们就得想办法去解决它。

而现在普遍的解决方案,就是多线程:

于是就出现下面这样的场景:

在这里插入图片描述
是不是觉得巨麻烦,竟然要从头到尾经历这么多的过程,才能最终,实现一个不起眼的缓存穿透!

假设:

所以说,通过双线程的加锁操作,是可以解决缓存击穿的问题的。

对于单个 redis 来说,上面的知识点已经可以实现分布式锁了。

但是,既然要讨论高并发高可用的系统,就会涉及到集群。

对于单个 redis 来说,假设,加锁的 redis 挂了,那该怎么办?

所以,这把锁很可能就丢了。

为了能够解决这样的问题,Redis 的作者 antirez 给出了一个更好的实现,称为 Redlock,算是 Redis 官方对于实现分布式锁的指导规范。

如果你们不善于阅读英文,那么就直接看我中文的描述:

在算法的分布式版本中,我们假设有 N 个 redis,且这些节点是完全独立的,也就是不存在任何主从关系,一个 redis 的死活和其他 redis 没有任何关系。

那么,应该给几台结点加锁呢?

所以,在整个加锁过程中,整个加锁的过程,不能超过锁的有效时间,否则,就应算作加锁失败,要立刻清除所有单独结点上的锁。

在这里插入图片描述
现在,想来你应该能大致理解,Redlock 加锁的大致过程了,下面我就用简略的语言,翻译一下官方对于 Redlock 的加锁操作:

看起来似乎很完美了,但是,我继续抛出一个问题。

假设一共有 5 个 redis ,分别是 ABCDE:

这是为什么呢?

因为这种情况在异步通信模型中是有可能发生的:客户端向服务器通信是正常的,但反方向却是有问题的。

那就算一个 key 失效,也会对数据库造成很大的影响,那么你把雪崩的所有 key 拆成一个一个 key 来看,也就是雪崩可以拆分成一个一个缓存击穿的集合。

那么既然缓存击穿已经给过解决方案了,那么我们现在要关注的,则是如何缓解雪崩所带来的压力。

首先一个很常见的做法就是,分散 key 的过期时间。

看起来就很像一个削峰的操作。

这个方法,是最简单有效的。所以一般情况,我们都采用这种方式。

在这里插入图片描述
不过,要考虑一种情况,就是,如果你的业务对时点性要求高,必须每天的指定时间,去更新我们的数据。
就比如游戏每日零点更新,或者财报记录……等等等等。

像这样的业务应该怎么办?

这样可行吗?

所以,对于读请求,不适合用队列的方式,因为这已经把请求串行化了,不再是并发执行。

于是,还有人提出观点,让缓存提前开始更新。

所以,缓存是必须要在 12 点准时失效,准时更新的。你无法让更新时间进行变化。

那么,你还能想到什么办法?

既然 redis 不可以,那么其它地方可以吗?

所以,这就是考验你思维和功力的时候。

既然 redis 无法分散过期时间,那么,我们去查数据的时候,是不是可以把时间稍微地分散一下?

所以到了下面这种情景:

在这里插入图片描述
这就是,从业务层,再把时间分散。

带来的影响,也就是客户等待时,会多那么几十毫秒的延迟,不过对于人来说,是微乎其微,可以接受的。

所以,对于时点性要求高的业务要求,雪崩的问题,想要解决,还必须稍微多思考,变通一下。

所以,缓存击穿可以规避,因为只是 redis 缓存数据失效了,而数据库里有数据,只要把数据库里的数据更新到 redis 上,那么就可以解决掉缓存击穿的问题。

那么,想要解决缓存穿透,就必须想办法,能够识别出,哪些请求的数据,是数据库没有的,然后,对这些请求的查询,进行过滤。

如果你以前没有了解过这些知识,那你可以先想一想,可以用什么办法?

所以,是不是就可以额外开辟一片 Set,用来专门存储 key,这样,每次要访问数据库前,先去 key set 中查询时候存在,如果存在,那么再去访问数据库。

这样,确实可以使得缓存不会穿透了。而且相比缓存全量 key、value,只存储 key 会使得内存的占用变小了很多。

所以,到了数据面前,就能发现仍然是一个超高成本的方法。

那么,既然如果存储 key,空间仍然很大,那么我们能否想出一个更节省空间的存储方式?

一般有点经验的都会想到用 bit,也就是用一个位来存储,这已经是计算机中的一个最小的存储单位了。

由此可见,用 bitmap,确确实实可以达到对空间利用的极致。

看起来似乎很完美,既解决了空间的问题,又可以保证每一个 key,能够映射到一个槽位上。

但是,仔细一思考,就会发现,其实还有问题。

那么,该怎么办?

这里也是如此,既然不能解决冲突问题,那么,可以想办法,让冲突发生的概率更小,而不是去完全地让冲突消失。

所以,就可以延伸到布隆算法。

这样,只要对应出我们的需求,去调整 bitmap 的大小,以及哈希函数的个数,就可以得到不同的过滤的百分比,虽然可能出现漏网之鱼,不过那也已经是少之又少了。

在这里插入图片描述
不过,对于布隆过滤器,我们的使用还是需要去思考一下的。

不管怎样,至少,穿透的问题,似乎已经迎来了大结局。

你现在想一想,是不是这么一个情况!

那么,解决的话,可以用布谷鸟这样的,带删除功能的,来满足动态变化的需求。

很多时候,对于一个问题,不是去拘泥于这个问题,而是你能够,联想到这个问题所置身的场景,能够理解清楚,整个系统的环境,能够从一个高的维度,去看这一系列的过程。

所以,对于我们来说,重要的,不是去背过这些答案,而是能够从一个系统、一个架构的角度,去理清设计的原由和思路。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/206011.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午4:41
下一篇 2026年3月19日 下午4:41


相关推荐

  • Ubuntu下代替dos2unix命令

    Ubuntu下代替dos2unix命令Ubuntu系统打开Windows下生成的文本文件,会在每行的末尾出现’^M’原因就是Windows和Linux的回车符是不同的在Windows下回车符是\r\n回车换行在Linux下回车符是\n最简单、最常用的解决方法是使用dos2unix命令转换:dos2unixfilenameUbuntu下dos2unix和unix2dos命令在tofrodos包中

    2022年7月14日
    20
  • python 初中课程_8年级将新增Python课程内容

    python 初中课程_8年级将新增Python课程内容从2020年9月1日开始,浙江3~9年级信息技术课程将替换新的教材。其中变化较大的是,8年级将新增Python课程内容。这也就意味着:从今天起,Python正式成为浙江中学生信息技术必修课!新高一信息技术编程语言由VB替换为Python,大数据、人工智能、程序设计与算法。网友们直呼:我连Excel都还没整明白,人家后浪们就开始学Python了。从的教材目录上我们可以看到,不止是初中,从小学阶段开始…

    2022年5月17日
    45
  • 正余弦定理公式

    正余弦定理公式正余弦定理公式

    2026年3月19日
    2
  • C之 十九 使用WinForm控件

    C之 十九 使用WinForm控件十九使用 WinForm 控件比如说电脑有显示器 鼠标 主机以及键盘的基本元素组成 在 windows 窗体中也有其基本控件 这些控件在每一个窗体中都要用到 也就是说无所不在 有些控件可能外观不同但是他们的使用方式都基本上一样 重点 掌握这些控件常用属性方法以及事件 能用编码的形式实

    2026年3月26日
    2
  • 常见股票代码开头说明大全

    常见股票代码开头说明大全股票代码用数字表示股票的不同含义 股票代码除了区分各种股票 也有其潜在的意义 一个公司的股票代码跟车牌号差不多 能够显示出这个公司的实力以及知名度 具体规范文档查看本文结尾附件常见证券编码规则 600 开头的股票是上证 A 股 属于大盘股 其中 6006 开头的股票是最早上市的股票 6016 开头的股票为大盘蓝筹股 900 开头的股票是上证 B 股 000 开头的股票是深证 A 股 001 002 开头

    2026年3月19日
    3
  • vsftp怎么用_c++ ftp

    vsftp怎么用_c++ ftp一、vsftp以及安装服务端软件:vsftpd客户端软件:ftp端口号:20、21或指定范围内其他随机端口配置文件:vim/etc/vsftpd/vsftpd.conf#安装yuminstallvsftpdftp#开机自启systemctlenablevsftpd#启动systemctlstartvsftpd#查看端口开放情况netstat-antp二、工作模式,默认是主动port模式①主动模式(prot):FTP.

    2026年3月6日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号