在开发中会面临缓存异常可能会出现三个问题,分别是缓存雪崩、缓存击穿和缓存穿透。这三个问题会导致大量请求从缓存转移到数据库,如果请求的并发量很大的话,就会导致数据库崩溃。所以在面试官也会经常问这些问题。

缓存雪崩

缓存雪崩是指大量的请求无法在缓存中处理,从而将请求转移到数据库中,导致数据压力倍增。一个Redis实例可以支持万级别的并发请求,而单个数据库只能支持千级别的并发请求。两者处理请求并发能力相差十倍,数据库会由于压力过大而导致雪崩。

这里雪崩一般是由两个原因组成,很多文章只写缓存同时过期的情况。

原因一:缓存中大量的数据同时过期

一般设置缓存数据会设置缓存时间,在某一时刻,大量的缓存同时过期,此时如果有请求访问这些数据的话,缓存不存在,会将请求转移到数据库,如果这些的请求量比较大的,导致数据库的压力增大,严重会导致数据库崩溃。

针对大量缓存同时失效带来的雪崩,有两种解决方案。

方案一: 过期时间设置随机值

应该避免给数据设置相同的过期时间,在设置过期时间时,增加一点随机值。

setRedis(key, value, time + Math.random() * 10000);

方案二: 服务降级

服务降低,比如使用hystrix,是指发生雪崩时,针对不同的数据采取不同的处理方式。

  • 请求数据是非核心数据(比如商品属性),暂时停止从缓存查询数据,直接返回预定信息、空值或者错误值。
  • 请求数据是核心数据(比如商品库存、价格),仍然查询缓存,如果缓存缺失,继续在数据库读取。

原因二: redis 服务挂了

redis服务发生宕机,无法处理请求,这就会导致全部转移到数据库去,发生雪崩。

方案一: 添加服务熔断

服务熔断,就是发生雪崩时,暂停对缓存的访问。等redis服务恢复正常后,再允许访问缓存。

对redis所在的服务器进行指标监控,比如QPS、CPU使用率、内存使用率等,如果发现redis服务宕机,而数据库请求压力倍增,此时可以启动熔断机制,暂停对缓存和数据库的访问,比如使用Hystrix服务熔断。

但是暂停对缓存系统的访问,但是对整个业务系统影响很大,导致很多数据不能查看。为了减少这种影响还有另一个方案:请求限流。

方案二: 请求限流

请求限流,就是限制前端请求每秒请求量,使得数据库能承受前端全部请求。

比如前端允许每秒访问1000次,其中900请求缓存,100次才请求数据库。

一旦发生雪崩,数据库每秒请求激增到1000次,此时启动请求限流,在前端入口只允许每秒请求100次,过多的请求直接拒绝。



无论使用服务熔断或请求限流都是是发生雪崩后处理,这里还有事先预防的方案。

通过主从节点的方式构建redis集群,如果redis主节点宕机,从节点可以切换成主节点。

缓存击穿

缓存击穿是指,针对某个访问缓存非常频繁,无法在缓存中处理,访问该数据的请求一下子都请求数据库,导致数据库压力倍增。

方案一:不设置过期时间

对于访问特别频繁的热点数据,就不设置过期时间

方案二:使用互斥锁

如果缓存失效,只有拿到锁才能访问数据库,降低数据库并发访问。

缓存雪崩和缓存击穿的差别在于雪崩是大量的缓存,击穿是单一的缓存。

缓存穿透

缓存穿透是指访问的数据既不在redis缓存中,也不在数据库中,因为数据库也不存在数据,也无法将数据库数据写入缓存中,每次请求都要请求缓存和服务器。不过这样也导致系统性能下降。

缓存穿透会发生如下两种情况:

  • 误操作,删除了缓存和数据的数据。
  • 恶意攻击: 专门访问数据库中不存在的数据。

方案一: 缓存空值或缺省值

发生缓存穿透,在redis中缓存一个空值或者实现预选设置好的值(比如0),后续请求查询直接在redis中读取空值或者缺省值。

方案二: 使用布隆过滤器

  • 布隆过滤器由一个初值都为0的bit数组和N个哈希函数组成,可以用来快速判断某个数据是否存在。当数据写入数据库时,布隆过滤器会通过三个操作完成标记:

    • 使用N个hash函数,分别计算这个数据的hash值,得到N个hash值。
    • 把这N个hash值对bit数组的长度取模,得到每个hash值在数组中对位置。
    • 把对应位置设置为1。如果数据不存在,那么就没用使用布隆过滤器标记过数据,那么,bit数组对应的bit位为零。只要bit数组有一个不为1,就表明布隆过滤器就没标记过该数据。
    • 把数据写入数据库时,使用布隆过滤器做标记
    • 当缓存消失后,在去数据库查询之前,通过查询布隆过滤器判断数据是否存在,如果不存在,就不查询数据库。

      还有在请求入库添加检测,把恶意请求(参数不合理、参数非法、参数不存在或者id小于0)直接过滤掉。

电商系统举例

  • 缓存雪崩

    * 电商首页数据(电脑端、手机端),比如分类。设置这一类的缓存需要给过期时间添加随机数

  • 缓存击穿
    • 电商首页的猜你喜欢商品,不设置超时时间,或者设置互斥锁

  • 缓存穿透
    • 电商商品详情中请求不存在的id,首先要设置入口验证,然后使用布隆过滤器,不存在直接返回

总结

缓存雪崩和缓存击穿主要是数据不在缓存上,而缓存穿透是数据既不在缓存上,也不在数据上。

  • 缓存雪崩

    • 给过期时间加上小的随机数
    • 服务降级
    • 服务熔断
    • 请求限流
    • redis 设置主从集群
  • 缓存击穿
    • 不设置过期时间
  • 缓存穿透
    • 入口进行合法性验证
    • 使用空值或者缺省值
    • 使用布隆过滤器快速判断

预防方案

使用服务降低、请求熔断、请求限制会影响用户使用体验,最好使用预防方案。

  • 针对缓存雪崩,合理设置数据过期时间,以及搭建redis主从集群。
  • 针对缓存击穿,不要设置过期时间。
  • 针对缓存穿透,在请求入口做规范校验,以及使用布隆过滤器判断数据是否存在。

参考

Redis 面试常见问题———缓存雪崩、缓存击穿以及缓存穿透的更多相关文章

  1. 老司机带你玩转面试(2):Redis 过期策略以及缓存雪崩、击穿、穿透

    前文回顾 建议前一篇文章没看过的同学先看下前面的文章: 「老司机带你玩转面试(1):缓存中间件 Redis 基础知识以及数据持久化」 过期策略 Redis 的过期策略都有哪些? 在聊这个问题之前,一定 ...

  2. Redis缓存雪崩、击穿、穿透

    参考大佬 前言 Redis在互联网技术存储方面使用如此广泛,几乎所有的后端技术面试官都要在Redis的使用和原理方面对小伙伴们进行360°的刁难.作为一个在互联网公司面一次拿一次offer的面霸(请允 ...

  3. Redis系列三 - 缓存雪崩、击穿、穿透

    前言 从学校出来,做开发工作也有一定时间了,最近有想系统地进一步深入学习,但发现基础知识不够扎实,故此来回顾基础知识,进一步巩固.加深印象. 最初开始接触编程时,总是自己跌跌撞撞.不断摸索地去学习,再 ...

  4. 第三节:Redis缓存雪崩、击穿、穿透、双写一致性、并发竞争、热点key重建优化、BigKey的优化 等解决方案

    一. 缓存雪崩 1. 含义 同一时刻,大量的缓存同时过期失效. 2. 产生原因和后果 (1). 原因:由于开发人员经验不足或失误,大量热点缓存设置了统一的过期时间. (2). 产生后果:恰逢秒杀高峰, ...

  5. Redis面试常见问题(一)

    一.redis 简介简单来说 redis 就是一个数据库,不过与传统数据库不同的是 redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向.另外,redis 也经 ...

  6. (转)面试前必知Redis面试题—缓存雪崩+穿透+缓存与数据库双写一致问题

    背景:redis问题在面试过程中经常被问到,对于常见问题一定不能放过. 面试前必知Redis面试题—缓存雪崩+穿透+缓存与数据库双写一致问题 一.缓存雪崩 1.1什么是缓存雪崩? 如果缓存数据设置的过 ...

  7. Redis缓存穿透、缓存击穿以及缓存雪崩

    作为一个内存数据库,redis也总是免不了有各种各样的问题,这篇文章主要是针对其中三个问题进行讲解:缓存穿透.缓存击穿和缓存雪崩.并给出一些解决方案.这三个问题是基本问题也是面试常问问题. 这篇文章我 ...

  8. 面试前必知Redis面试题—缓存雪崩+穿透+缓存与数据库双写一致问题

    今天来分享一下Redis几道常见的面试题: 如何解决缓存雪崩? 如何解决缓存穿透? 如何保证缓存与数据库双写时一致的问题? 一.缓存雪崩 1.1什么是缓存雪崩? 回顾一下我们为什么要用缓存(Redis ...

  9. 什么是redis缓存穿透, 缓存雪崩, 缓存击穿

    什么是redis? redis是一个非关系型数据库,相对于其他数据库而言,它的查询速度极快,且能承受的瞬时并发量非常的高.所以常常被用来存放网站的缓存,以减少主要数据库(如mysql)的服务器压力. ...

随机推荐

  1. MySQL alter table时执行innobackupex全备再看Seconds_Behind_Master

    1.场景描述 早上7:25 接到Report中心同学告警,昨天业务报表数据没有完整跑出来,缺少500位业务员的数据,并且很快定位到,缺少的是huabei_order库上的数据.Report中心的数据是 ...

  2. PyQT5:信号和槽

    PyQT5:信号和槽 信号和槽 Qt的主要特征之一是它使用信号和插槽在对象之间进行通信. 当潜在的事件发生时,会发出一个信号.插槽是可调用的Python,如果将信号连接到插槽,则在发出信号时将调用该插 ...

  3. markdown的骚气操作(一)

    markdown 系列其他内容   markdown的骚气操作(一)✓   latex的骚气操作(二) 本文目标 主要介绍markdown锚点.索引脚注.对勾及选择框.表格显示位置和符号显示位置.绘制 ...

  4. jq的选择器中带有特殊符号无法获取元素

    因项目需要,将元素id命名为数组(array[i].string) 使用jq去获取该id的元素时,返回的是个undefined.即jq获取不到该元素,因为该元素中的id含有特殊字符"[&qu ...

  5. JS021. 拦截事件的显式处理与默认动作(Web API: event.preventDefault)

    Web API - event.preventDefault( ) Event 接口的  preventDefault( ) 方法,告诉 user agent :如果此事件没有被显式处理,它默认的动作 ...

  6. 推荐一款编程字体:Iosevka

    最近发现一款很好用的编程字体:Iosevka.它是一款现代化的编程字体集合,除了等宽.oO0 iIl1明显区分等基本特性外,还有很多非常现代的特性,比如: 多种风格:有非常多的字形可供选择,衬线/非衬 ...

  7. 对easyui-validatebox的验证类型的扩展

    easyui为我们提供了validatebox类型的组件,使用它可以完成自动验证,十分方便.要注意的是,easyui中的各个组件都是有继承关系的.通过查看api,textbox继承validatebo ...

  8. 前端框架VUE——安装及初始化

    本篇文章适合,想要学习 vue,但对 vue 又没有接触过的同学阅读,是非常基础的内容.告诉大家使用 vue 时的安装方式,及如何创建实例,展示内容. 一.安装方式 vue 是一种前端框架,所以使用前 ...

  9. Django学习day12随堂笔记

    每日测验 """ 1.什么是cookie和session,你能描述一下它们的由来和工作机制吗(切勿糊弄,敷衍了事) 2.django中如何操作cookie和session ...

  10. PHP7兼容mysql_connect的方法

    在php7版本的时候,mysql_connect已经不再被支持了,本文将讲述在代码层面实现php7兼容mysql系列,mysql_connect等操作. PHP7不再兼容mysql系列函数,入mysq ...