浅谈Redis缓存击穿、缓存穿透、缓存雪崩的解决方案

目录
  • 前言
  • Redis缓存使用场景
  • Redis缓存穿透
    • 解决方案
      • 1.对空值缓存
      • 2.添加参数校验
      • 3.采用布隆过滤器
  • Redis缓存雪崩
    • 解决方案
      • 1.大量热点数据同时失效带来的缓存雪崩问题
      • 2. 服务降级
      • 3. Redis 缓存实例发生故障宕机带来的缓存雪崩问题
  • Redis缓存击穿
    • 解决方案
      • 1. 热key不过期
      • 2. 分布式锁
  • 总结
    • 缓存击穿
    • 缓存穿透
    • 缓存雪崩

前言

在日常的项目中,缓存的使用场景是比较多的。缓存是分布式系统中的重要组件,主要解决在高并发、大数据场景下,热点数据访问的性能问题,提高性能的数据快速访问。本文以Redis作为缓存时,针对常见的缓存击穿、缓存穿透、缓存雪崩问题做简单地说明,并且提供有效的解决方案。

Redis缓存使用场景

Redis会把数据库中经常被查询的数据缓存起来,比如热点数据,这样当用户通过网站或APP来访问的时候,就不需要到数据库中去查询了,而是直接获取 Redis中的缓存数据,从而降低了后端数据库的读取压力。如果说用户查询的数据Redis中没有,此时用户的查询请求就会转到数据库,当数据库将数据返回给客户端时,同时会将数据缓存到 Redis中,这样用户再次读取时,就可以直接从Redis中获取数据。流程图如下所示:

Redis缓存穿透

缓存穿透是指用户恶意的发起大量请求去查询一个缓存(Redis)和数据库(DB)中都没有的数据,出于容错考虑从数据库(DB)查不到数据则不写入缓存(Redis)这将导致每个请求都要到数据库(DB)中查询,失去了缓存的意义,从而导致数据库因压力过大挂掉。

流程图如下所示:

解决方案

1.对空值缓存

上面我们也介绍了,之所以会发生穿透,是因为缓存中没有存储这些空数据的key,从而导致每次查询都到数据库去了。

那么我们就可以为这些key的值设置null丢到缓存里面去,后面再出现查询这个key 的请求的时候,直接返回null ,就不用在到数据库中去走一圈了。但是别忘了设置过期时间。

关键代码如下:

2.添加参数校验

我们可以在接口层添加校验,不合法的直接返回即可,没必要做后续的操作。

例如:使用bitmaps类型定义一个可以访问名单,名单id作为bitmaps的偏移量,每次访问时与bitmaps中的id进行比较,如果访问id不在bitmaps中,则进行拦截,不给其访问。

3.采用布隆过滤器

布隆过滤器(Bloom Filter),Bloom Filter 类似于一个hash set 用来判断某个元素(key)是否存在于某个集合中,不存在return就好了,存在就去查DB刷新缓存KV再return,它的优点是空间效率和查询时间都比一般算法快,缺点是有一定的误识别率和删除困难。

布隆过滤器的工作方式:

一个空的布隆过滤器是一个由m个二进制位构成的数组。

以上只是画了布隆过滤器的很小很小的一部分,实际布隆过滤器是非常大的数组(这里的大是指它的长度大,并不是指它所占的内存空间大)。

当一个数据进行存入布隆过滤器的时候,会经过若干个哈希函数进行哈希,得到对应的哈希值作为数组的下标,然后将初始化的位数组对应的下标的值修改为1,结果图如下:

当再次进行存入第二个值的时候,修改后的结果的原理图如下:

那么为什么会有误判率呢?

假设在我们多次存入值后,在布隆过滤器中存在x、y、z这三个值,布隆过滤器的存储结构图如下所示:

当我们要查询的时候,比如查询M这个数,实际中M这个数是不存在布隆过滤器中的,经过哈希函数计算后得到M的哈希值分别为1和7,结构原理图如下:

经过查询后,发现1和7位置所存储的值都为1,但是1和7的下标分别是X和Z经过计算后的下标位置的修改,该布隆过滤器中实际不存在M,那么布隆过滤器就会误判改值可能存在,因为布隆过滤器不存元素值,所以存在误判率。

那么为什么不能删除元素呢?

原因很简单,因为删除元素后,将对应元素的下标设置为零,可能别的元素的下标也引用改下标,这样别的元素的判断就会受到影响。

Redis缓存雪崩

缓存雪崩是指大量的应用请求无法在Redis缓存中进行处理,紧接着应用将大量请求发送到数据库层,导致数据库层的压力激增。

缓存雪崩一般是由两个原因导致的,应对方案也有所不同。第一个原因是:缓存中有大量数据同时过期,导致大量请求无法得到处理。第二个原因是:Redis 缓存实例发生故障宕机了,无法处理请求,这就会导致大量请求一下子积压到数据库层,从而发生缓存雪崩。

流程图如下所示:

解决方案

1.大量热点数据同时失效带来的缓存雪崩问题

避免热key同时失效

使用 EXPIRE命令给每个数据设置过期时间时,给这些数据的过期时间增加一个较小的随机数(例如,随机增加 1~3 分钟)。这样一来,不同数据的过期时间有所差别,但差别又不会太大。既避免了大量数据同时过期,同时也保证了这些数据基本在相近的时间失效,仍然能满足业务需求。

2. 服务降级

所谓的服务降级,是指发生缓存雪崩时,针对不同的数据采取不同的处理方式,例如:

当业务应用访问的是非核心数据时,暂时停止从缓存中查询这些数据,而是直接返回预定义信息、空值或是错误信息;

当业务应用访问的是核心数据时,仍然允许查询缓存,如果缓存缺失,也可以继续通过数据库读取。

这样一来,我们就避免了大量请求因缓存缺失,而积压到数据库系统,保证了数据库系统的正常运行。

3. Redis 缓存实例发生故障宕机带来的缓存雪崩问题

从事前预防的角度,我们可以通过主从节点的方式构建 Redis 缓存高可靠集群。如果 Redis缓存的主节点故障宕机了,从节点还可以切换成为主节点,继续提供缓存服务,避免了由于缓存实例宕机而导致的缓存雪崩问题。

如果实际业务系统真发生了Redis 缓存实例不可用的情况,我们可以在业务系统中实现服务熔断或请求限流机制。所谓的服务熔断,是指在发生缓存雪崩时,为了防止引发连锁的数据库雪崩,甚至是整个系统的崩溃,我们暂停业务应用对缓存系统的接口访问。

Redis缓存击穿

我们在平常高并发的系统中,大量的请求同时查询一个key时,假设此时这个key正好失效了,就会导致大量的请求都打到数据库上面去,这种现象我们称为击穿。

这么看缓存击穿和缓存雪崩有点像,但是又有一点不一样,缓存雪崩是因为大面积的缓存失效,打崩了DB,而缓存击穿不同的是「缓存击穿」是指一个Key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞,如下图所示:

解决方案

1. 热key不过期

预先设置热门数据:在Redis高峰访问时期,提前设置热门数据到缓存中,对这些热key不设置失效时间,不过这样设置需要区分场景。

实时调整:实时监控哪些数据热门,实时调整key过期时间。

2. 分布式锁

为了避免出现缓存击穿的情况,我们可以在第一个请求去查询数据库的时候对他加一个分布式锁,其余的查询请求都会被阻塞住,直到锁被释放,后面的线程进来发现已经有缓存了,就直接走缓存,从而保护数据库。但是也是由于它会阻塞其他的线程,此时系统吞吐量会下降。需要结合实际的业务去考虑是否要这么做。

关键代码如下:

总结

缓存击穿

key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。一般通过互斥锁,热点数据永不过期,定时刷新过期时间等方法解决该问题。

缓存穿透

key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。一般通过对空数据进行缓存,布隆过滤器等方法解决该问题。

缓存雪崩

当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。一般通过加锁排队,设置过期时间随机值等方法解决该问题。

到此这篇关于浅谈Redis缓存击穿、缓存穿透、缓存雪崩的解决方案的文章就介绍到这了,更多相关Redis缓存击穿解决方案内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 关于Redis bigkeys命令会阻塞问题的解决

    目录 前言 一. 顺丰高级开发工程师在线执行了 Redis 危险命令导致某公司损失 400 万 二.测试一下1000万数据的性能 1.编写脚本文件 2.写入Redis1000万数据 3.通过keys * 查看1000万数据 4.通过配置文件禁止keys *的使用 三.使用scan替代keys * 四.拒绝bigkey 1.阿里云Redis开发规范 2.出现bigkey时如何删除? 3.bigkey会造成哪些问题? 4.如何发现bigkey? 前言 今天分享一次Redis引发的线上事故,避免再次踩

  • Redis与MySQL的双写一致性问题

    目录 更新缓存? 删除缓存? 先更新缓存再更新数据库 先更新数据库,再更新缓存 先删除缓存再更新数据库 先更新数据库,再删除缓存 解决方案 1. 重试 2. 异步重试 2.1 使用消息队列实现重试 2.2 Binlog实现异步重试删除 3. 延时双删 总结 Redis与MySQL双写一致性是指在使用缓存和数据库同时存储数据的场景下( 主要是存在高并发的情况),如何保证两者的数据一致性(内容相同或者尽可能接近). 正常业务流程: 读没什么问题,关键就在于写(更新)操作,这就会出现几个问题了,这里是

  • python中sub-pub机制实现Redis的订阅与发布

    先介绍一下redis的pub/sub功能: Pub/Sub功能(means Publish, Subscribe)即发布及订阅功能.基于事件的系统中,Pub/Sub是目前广泛使用的通信模型,它采用事件作为基本的通信机制,提供大规模系统所要求的松散耦合的交互模式:订阅者(如客户端)以事件订阅的方式表达出它有兴趣接收的一个事件或一类事件:发布者(如服务器)可将订阅者感兴趣的事件随时通知相关订阅者. 通俗来讲,就是说我sub端(订阅者)一直监听着,一旦pub端(发布者)发布了消息,那么我就接收过来,举

  • 浅谈一下如何保证Redis缓存与数据库的一致性

    目录 1.四种同步策略: 2.更新缓存还是删除缓存 2.1 更新缓存 2.2 删除缓存 3.先操作数据库还是缓存 3.1 先删除缓存再更新数据库 3.2 先更新数据库再删除缓存 最终结论: 4.延时双删 4.1 采用读写分离的架构怎么办? 5.利用消息队列进行删除的补偿 1.四种同步策略: 想要保证缓存与数据库的双写一致,一共有4种方式,即4种同步策略: 先更新缓存,再更新数据库: 先更新数据库,再更新缓存: 先删除缓存,再更新数据库: 先更新数据库,再删除缓存. 从这4种同步策略中,我们需要作

  • 浅谈Redis缓存击穿、缓存穿透、缓存雪崩的解决方案

    目录 前言 Redis缓存使用场景 Redis缓存穿透 解决方案 1.对空值缓存 2.添加参数校验 3.采用布隆过滤器 Redis缓存雪崩 解决方案 1.大量热点数据同时失效带来的缓存雪崩问题 2. 服务降级 3. Redis 缓存实例发生故障宕机带来的缓存雪崩问题 Redis缓存击穿 解决方案 1. 热key不过期 2. 分布式锁 总结 缓存击穿 缓存穿透 缓存雪崩 前言 在日常的项目中,缓存的使用场景是比较多的.缓存是分布式系统中的重要组件,主要解决在高并发.大数据场景下,热点数据访问的性能

  • 浅谈Redis 缓存的三大问题及其解决方案

    目录 一.缓存穿透 1. 常见解决方案 2. 布隆过滤器 3. 缓存空数据与布隆过滤器的比较 二.缓存击穿 解决方案 三.缓存雪崩 解决方案 Redis 经常用于系统中的缓存,这样可以解决目前 IO 设备无法满足互联网应用海量的读写请求的问题. 一.缓存穿透 缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起 id 为-1 的数据或者特别大的不存在的数据.有可能是黑客利用漏洞攻击从而去压垮应用的数据库. 1. 常见解决方案 对于缓存穿透问题,常见的解决方案有以下三种: 验证拦截:

  • 浅谈Redis高并发缓存架构性能优化实战

    目录 场景1: 中小型公司Redis缓存架构以及线上问题实战 场景2: 大厂线上大规模商品缓存数据冷热分离实战 场景3: 基于DCL机制解决热点缓存并发重建问题实战 场景4: 突发性热点缓存重建导致系统压力暴增 场景5: 解决大规模缓存击穿导致线上数据库压力暴增 场景6: 黑客工资导致缓存穿透线上数据库宕机 场景7: 大V直播带货导致线上商品系统崩溃原因分析 场景8: Redis分布式锁解决缓存与数据库双写不一致问题实战 场景9: 大促压力暴增导致分布式锁串行争用问题优化 场景10: 利用多级缓

  • 浅谈redis缓存在项目中的使用

    背景 Redis 是一个开源的内存数据结构存储系统. 可以作为数据库.缓存和消息中间件使用. 支持多种类型的数据结构. Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence). 通过 Redis 哨兵(Sentinel)和 Redis 集群(Cluster)的自动分区,提供高可用性(high availability). 基本数

  • 浅谈Redis缓存有哪些淘汰策略

    目录 Redis过期策略 定时删除 惰性删除 定期删除 Redis的内存淘汰机制 LRU和LFU的区别 LRU LFU Redis重启如何恢复数据呢? 总结 Redis过期策略 我们首先来了解一下Redis的内存淘汰机制. 定时删除 概述     redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除.注意这里是随机抽取的.为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会

  • 浅谈Redis缓存雪崩解决方案

    目录 1.保持缓存层的高可用 2.限流降级组件 3.缓存不过期 4.优化缓存过期时间 5.使用互斥锁重建缓存 6.异步重建缓存 缓存层承载着大量的请求,有效保护了存储层.但是如果由于大量缓存失效或者缓存整体不能提供服务,导致大量的请求到达存储层,会使存储层负载增加(大量的请求查询数据库) .这就是缓存雪崩的场景; 解决缓存雪崩可以从下面的几点着手: 1.保持缓存层的高可用 使用Redis哨兵模式或者Redis集群部署方式,即是个别Redis节点下线,整个缓存层依然可以使用.除此之外还可以在多个机

  • 浅谈Redis缓存更新策略

      内存淘汰 超时剔除 主动更新 说明 不用自己维护,利用Redis的内存淘汰机制,当内存不足时自动淘汰部分数据.下次查询时更新缓存 给缓存数据添加TTL时间,到期后自动删除缓存,下次查询时更新缓存 编写业务逻辑,在修改数据的同时,更新缓存 一致性 差 一般 好 维护成本 无 低 高 业务场景需求: 在基本不会更新数据的情况下可以使用内存淘汰机制 在频繁更新数据的情况下可以使用主动更新,并以超时剔除作为兜底方案. 主动更新的三种方法 Cache Aside Pattern:由缓存的调用者,在更新

  • 浅谈JSON的数据交换、缓存问题和同步问题

    JSON轻量级的数据交换格式 相对于XML来说,JSON的解析速度更快,文档更小. JSON的格式 {属性名:属性值,属性名:属性值,--} 属性名的类型可以是string,number,boolean,null,object,且属性名必须用双引号引起来,如果属性值是字符串,也必须用双引号括起来. JSON表示数组 格式:[value,value,value],其中value可以是基本的数据类型,也可以是object类型.数组类型 数组类型 [ {"name":"yangjq

  • 浅谈Redis跟MySQL的双写问题解决方案

    目录 写在前面 三种读写缓存策略 Cache-AsidePattern(旁路缓存模式) Read-Through/Write-Through(读写穿透) WriteBehindPattern(异步缓存写入) 旁路缓存模式解析 CacheAsidePattern的一些疑问 CacheAsidePattern的缺陷 项目中有遇到这个问题,跟MySQL中的数据不一致,研究一番发现这里面细节并不简单,特此记录一下. 写在前面 严格意义上任何非原子操作都不可能保证一致性,除非用阻塞读写实现强一致性,所以缓

  • 浅谈redis在项目中的应用

    redis在项目中的应用 ps:PHP 会自动 关redis连接 不需要手动关 对于临时的数据 可以不经过数据库直接redis上操作 /*消息队列实例 */ public function insertinfo(){ //连接本地的 Redis 服务 $redis = new \Redis(); $redis->connect('127.0.0.1', 6379); //存储数据到列表中 $infos = array('info1' => 66, 'info2' => 88); $red

随机推荐