不用背八股文!一文搞懂redis缓存击穿、穿透、雪崩!
mhr18 2024-11-05 10:23 40 浏览 0 评论
缓存的击穿、穿透和雪崩,对于这三大缓存的问题,有很多人背过了八股文式的解决方案,面试也能答上一二,却少有人能把思路给理清的。
这三个问题产生的前提是高并发,但是一旦发生,会导致大量的请求积压到数据库层,并发量很大,甚至会引起数据库宕机或是故障,造成严重的生产事故。下面我将一一分析,保证让你看得明白透彻!
系统架构
在解释缓存问题之前,我们先来复习一下系统架构,知道问题出在哪,才能找到解决之道。
一个大的spring cloud微服务集群,从客户端发送请求开始,会先经过F5、Nginx等流量分发层,然后到spring cloud gateway的服务网关,最后才到我们的后端服务service。而service会先从缓存查找数据,没有再去数据库找。
redis作为缓存,在整个系统体系中,就是将很多的请求扛住,过滤掉,从而使得最后的数据库的压力就会很小。很显然只有在大流量、高并发的时候,redis没能扛住访问请求,才会导致击穿、穿透、雪崩的出现。
缓存击穿
缓存击穿是指,针对某个访问非常频繁的热点数据的请求,无法在缓存中获取,紧接着,访问该数据的大量请求,一下子都发送到了后端数据库,导致了数据库压力激增,直接影响数据库无法处理其他请求。
说白了,就是某一个热点数据,缓存中某一时刻失效了,因而大量并发请求打到数据库上,就像redis被打了一个窟窿,被击穿了一样,此时,该数据redis中没有,数据库中才有。
解决方案
网上查到的方法都是说设置 key 永不过期,也就是不设置过期时间,但是数据总是会变的,那这时候该如何处理呢?思考以下几种方式,看是否有缺陷。
- 在更新数据库时,同时更新key
- 后台起一个定时任务,每隔一段时间,在 key 快要失效的时候,提前将 key 刷新为最新数据
- 每次获取 key 都检查,key 还有多久过期,如果快过期,则更新这个 key
这些方案其问题在于,更新操作不是原子性的,无法保证数据库和redis缓存数据的一致性,所以在实际的生产环境中,是无法胜任的。
这时候就可以考虑使用redis分布式锁,通过分布式锁来实现数据库和redis的数据同步。
具体操作如下:当service访问缓存key为空时,先加锁,加锁成功后往mysql写数据,接着更新缓存,然后释放锁;加锁失败,说明有其他service已经加上了锁,需等待或循环尝试查缓存和加锁。
但是加锁也有讲究,不是简单的setnx命令。如果加分布式锁的service在加锁成功后突然挂掉,那锁就无法释放,所以加分布式锁的同时,还要原子性的给锁设置超时时间。
其实redis的set命令就能够满足我们当前的需求:
SET key value [EX seconds] [PX milliseconds] [NX|XX]
其中,key是要存储的键名,value是要存储的值。EX和PX参数可选,用于设置键的过期时间,单位分别为秒和毫秒。NX和XX参数也可选,用于控制键的创建行为,NX表示只在键不存在时创建,XX表示只在键已存在时执行操作。
那这就万无一失了吗,答案肯定是否定的,分布式锁key的时候设置多长我们并不清楚,太短可能线程还没执行完业务逻辑,太长响应又过慢。为解决这一问题,需要引入redisson工具,利用redisson的看门狗机制来控制key的超时时间。
【文章福利】:免费领取C/C++ 开发学习资料包、技术视频/项目代码,1000道大厂面试题,内容包括(C++组件,网络编程,数据库,中间件,后端开发,音视频开发,Qt开发,游戏开发,Linux内核等进阶学习资料和最佳学习路线),免費分享,有需要的可以加君羊领取哦!~学习交流君羊927239107点击加入领取资料
缓存穿透
缓存穿透是指要访问的数据既不在 Redis 缓存中,也不在数据库中,导致请求在访问缓存时,发生缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据。此时,应用也无法从数据库中读取数据再写入缓存,来服务后续请求,这样一来,缓存也就成了“摆设”。
解决方案
第一种方案是,缓存空值或缺省值。一旦发生缓存穿透,可以针对查询的数据,在 Redis 中缓存一个特殊的缺省值。应用发送的后续请求再进行查询时,就可以直接从 Redis 中读取该值,返回给业务使用,避免了把大量请求发送给数据库处理,保持了数据库的正常运行。
不过,数据库中不存在的数据有很多,像依据前端入参作为key的缓存,如果每个都设置缺省值就会造成空间上的浪费,这时候就得考虑方案二。
第二种方案是,使用布隆过滤器快速判断数据是否存在,避免从数据库中查询数据是否存在,减轻数据库压力。布隆过滤器的底层数据结构是bitmap,即用一个比特位来表示一个key,花费 1 个字节的空间,就能存储 8 个 key。
一个比特位只能是0或1,无法表示更多信息,一个key经过一次哈希算法算的哈希值(即要落在第几个比特位)就有可能和其他key存在冲突。为解决冲突,就需要多次哈希,相应的一个key就需要多个比特位来存储。
这样,只要对应出我们的需求,去调整 bitmap 的大小,以及哈希函数的个数,就可以大大过滤出存在的key,降低缓存穿透的概率。
如果数据频繁增删改,是不太适合用布隆过滤器的。因为一个数据变更之后,布隆过滤器无法删除 key,因此,只能重新创建一个布隆过滤器,再加载一遍所有的数据,创建出新的bitmap。那么,解决的话,可以使用带删除功能的布谷鸟布隆过滤器,来满足动态变化的需求。
最后一种方案是,在请求入口的前端进行请求检测。缓存穿透的一个原因是有大量的恶意请求访问不存在的数据,所以,一个有效的应对方案是在请求入口前端,对业务系统接收到的请求进行合法性检测,把恶意的请求(例如请求参数不合理、请求参数是非法值、请求字段不存在)直接过滤掉,不让它们访问后端缓存和数据库。这样一来,也就不会出现缓存穿透问题了。
缓存雪崩
缓存雪崩,指的是大面积的 key 同时过期,导致大量并发打到我们的数据库。不像击穿,只是因为 1 个 key 的过期。那就算一个 key 失效,也会对数据库造成很大的影响,那么可以把雪崩的所有 key 拆成一个一个 key 来看,也就是雪崩可以拆分成一个一个缓存击穿的集合。
其实在真实场景中,雪崩才是一个更容易发生的一个问题,它不像击穿那么极端,一个 key 就成千上万的并发,直接把数据库打垮了;而是,可能就一个 key 几十几百的并发,然后大量的 key 一过期,然后就使得好多并发同时叠加起来,累积到上千上万个,把数据库打崩了。
解决方案
首先,我们可以避免给大量的数据设置相同的过期时间。如果业务层的确要求有些数据同时失效,可以在用 EXPIRE 命令给每个数据设置过期时间时,给这些数据的过期时间增加一个较小的随机数(例如,随机增加 1~3 分钟),这样一来,不同数据的过期时间有所差别,但差别又不会太大,既避免了大量数据同时过期,同时也保证了这些数据基本在相近的时间失效,仍然能满足业务需求。
除了微调过期时间,我们还可以通过服务降级,来应对缓存雪崩。
一旦发生了缓存雪崩,数据库的每秒请求数突然增加到每秒 1 万个,此时,我们就可以启动请求限流机制,在请求入口前端只允许每秒进入系统的请求数为 1000 个,再多的请求就会在入口前端被直接拒绝服务。所以,使用了请求限流,就可以避免大量并发请求压力传递到数据库层。
不过,要考虑一种情况,就是,如果你的业务对时点性要求高,必须每天的指定时间,去更新我们的数据,比如游戏排行每日零点更新。在某一个固定的时间,由于业务要求,必须使得数据刷新,并且不允许出现旧数据,让缓存全部失效。像这样的业务应该怎么办?
既然 redis 无法分散过期时间,那就在业务层下功夫。时间一到,redis 数据全部失效,大量并发前来查询,在 service 服务层查询时,设置一个短暂的随机延迟时间,这样,少量的请求,先查询,就会读数据库,然后存入 redis;其他请求,由于随机时间稍稍慢了点,就可以去 redis 读出数据。
相当于是从业务层把时间分散。客户可能会多那么几十毫秒的延迟,不过影响微乎其微,是可以接受的。
总结
从问题成因来看,缓存雪崩和击穿主要是因为数据不在缓存中了,而缓存穿透则是因为数据既不在缓存中,也不在数据库中。所以,缓存雪崩或击穿时,一旦数据库中的数据被再次写入到缓存后,应用又可以在缓存中快速访问数据了,数据库的压力也会相应地降低下来,而缓存穿透发生时,Redis 缓存和数据库会同时持续承受请求压力。
对于缓存的击穿、雪崩、穿透,看似很平常简单的问题,重要的,不是死记硬背八股文式的答案,而是能够从系统和架构的角度,去理清设计原由和解决思路。
相关推荐
- 【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库
-
如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...
- Pure Storage推出统一数据管理云平台及新闪存阵列
-
PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...
- 对Java学习的10条建议(对java课程的建议)
-
不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...
- SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!
-
官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...
- JDK21有没有什么稳定、简单又强势的特性?
-
佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...
- 「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了
-
在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...
- Java面试题及答案最全总结(2025版)
-
大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...
- 数据库日常运维工作内容(数据库日常运维 工作内容)
-
#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...
- 分布式之系统底层原理(上)(底层分布式技术)
-
作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...
- oracle 死锁了怎么办?kill 进程 直接上干货
-
1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...
- SpringBoot 各种分页查询方式详解(全网最全)
-
一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...
- 《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略
-
《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...
- LoadRunner(loadrunner录制不到脚本)
-
一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...
- Redis数据类型介绍(redis 数据类型)
-
介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...
- RMAN备份监控及优化总结(rman备份原理)
-
今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)