目录

  • 缓存在应用中的处理流程
    • 缓存击穿
      • 解决方案
    • 缓存穿透
      • 解决方案
        • 布隆过滤器
          • 写入流程
          • 查询流程
    • 缓存雪崩
      • 解决方案

缓存在应用中的处理流程

前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时再从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果


看似简单的处理流程;然而在一个分布式应用中,只要是引入了缓存,为了保证系统的健壮性,在缓存系统设计时,就必须要考虑缓存击穿,缓存穿透,缓存雪崩的问题及解决方案

缓存击穿

缓存击穿:缓存中没有数据,但数据库中有数据。(一般是缓存时间到期导致缓存失效)这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力,甚至可能压垮数据库

解决方案

  • 设置热点数据不过期:直接将缓存设置为不过期,然后由定时任务去异步加载数据,更新缓存。这种方式适用于比较极端的场景,例如流量特别特别大的场景,使用时需要考虑业务能接受数据不一致的时间,还有就是异常情况的处理,不要到时候缓存刷新不上,一直是脏数据,那就凉了
  • 加互斥锁:在并发的多个请求中,只有第一个请求(或前几个请求)线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,后面的请求直接从缓存中取数据。对于互斥锁的选择,可以是 Java API 层面的 Lock 或是 JVMsynchronized 或是分布式锁,只要保证数据库的请求能大大降低即可。注意加锁的维度要按 key 维度去加锁

使用分布式锁的伪代码,仅供参考

public Object getData(String key) throws InterruptedException {Object value = redis.get(key);// 缓存值过期if (value == null) {// lockRedis:专门用于加锁的redis;// "empty":加锁的值随便设置都可以if (lockRedis.set(key, "empty", "PX", lockExpire, "NX")) {try {// 查询数据库,并写到缓存,让其他线程可以直接走缓存value = getDataFromDb(key);redis.set(key, value, "PX", expire);} catch (Exception e) {// 异常处理} finally {// 释放锁lockRedis.delete(key);}} else {// sleep50ms后,进行重试Thread.sleep(50);return getData(key);}}return value;
}

缓存穿透

缓存穿透:查询一个数据,发现缓存中没有;于是查询数据库,发现也没有。而后面用户不断发起请求,缓存没有命中,于是都去请求了数据库。这给数据库造成很大的压力,引起数据库压力瞬间增大,造成过大压力,甚至可能压垮数据库

解决方案

  • 接口校验:在正常业务流程中可能会存在少量访问不存在 key 的情况,但是一般不会出现大量的情况,所以这种场景最大的可能性是遭受了非法攻击。可以在最外层先做一层校验:用户鉴权、数据合法性校验等,例如用户鉴权校验,id 做基础校验,id<=0 的直接拦截
  • 缓存空值:从缓存取不到数据,在数据库中也没有取到,这时也可以将 key-value 对写为 key-null,缓存有效时间可以设置短点,如 30 秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个 id 暴力攻击
  • 布隆过滤器:使用布隆过滤器存储所有可能访问的 key,不存在的 key 直接被过滤,存在的 key 则再进一步查询缓存和数据库

布隆过滤器

布隆过滤器的特点是判断不存在的,则一定不存在;判断存在的,大概率存在,但也有小概率不存在。并且这个概率是可控的,我们可以让这个概率变小或者变高,取决于用户本身的需求

布隆过滤器由一个 bitSet 和 一组 Hash 函数(算法)组成,是一种空间效率极高的概率型算法和数据结构,主要用来判断一个元素是否在集合中存在

在初始化时,bitSet 的每一位被初始化为 0,同时会定义 Hash 函数,例如有 3Hash 函数:hash1、hash2、hash3

写入流程

当我们要写入一个值时,过程如下,以 jionghui 为例

  • 首先将 jionghui3Hash 函数分别计算,得到 bitSet 的下标为:1、7、10
  • bitSet 的这 3 个下标标记为 1

假设我们还有另外两个值:javadiaosi,按上面的流程跟 3Hash 函数分别计算,结果如下

  • javaHash 函数计算 bitSet 下标为:1、7、11
  • diaosiHash 函数计算 bitSet 下标为:4、10、11

查询流程

当我们要查询一个值时,过程如下,同样以 jionghui 为例

  • 首先将 jionghui3Hash 函数分别计算,得到 bitSet 的下标为:1、7、10
  • 查看 bitSet 的这 3 个下标是否都为 1,如果这 3 个下标不都为 1,则说明该值必然不存在,如果这 3 个下标都为 1,则只能说明可能存在,并不能说明一定存在

其实上图的例子已经说明了这个问题了,当我们只有值 jionghuidiaosi 时,bitSet 下标为 1 的有:1、4、7、10、11。当我们又加入值 java 时,bitSet 下标为 1 的还是这 5个,所以当 bitSet 下标为 1 的为:1、4、7、10、11 时,我们无法判断值 java 存不存在

其根本原因是,不同的值在跟 Hash 函数计算后,可能会得到相同的下标,所以某个值的标记位,可能会被其他值给标上了。这也是为啥布隆过滤器只能判断某个值可能存在,无法判断必然存在的原因。但是反过来,如果该值根据 Hash 函数计算的标记位没有全部都为 1,那么则说明必然不存在,这个是肯定的

缓存雪崩

缓存雪崩:当某一时刻发生大规模的缓存失效的情况,比如你的缓存服务宕机了,会有大量的请求进来直接打到 DB 上,这样可能导致整个系统的崩溃,称为雪崩

解决方案

  • 过期时间打散:既然是大量缓存集中失效,那最容易想到就是让他们不集中生效。可以给缓存的过期时间时加上一个随机值时间,使得每个 key 的过期时间分布开来,不会集中在同一时刻失效
  • 热点数据不过期:该方式和缓存击穿一样,也是要着重考虑刷新的时间间隔和数据异常如何处理的情况

参考:https://blog.csdn.net/v123411739/article/details/115058811

关于缓存击穿、缓存穿透、缓存雪崩及解决方案相关推荐

  1. Redis常见面试题(缓存击穿、穿透、雪崩)

    Redis常见面试题(缓存击穿.穿透.雪崩) 击穿 场景: 一般由于redis中的数据到期,同时并发用户特别多,此时大量请求压到数据库上. 解决思路: 根据redis是单进程单实例的特性,当高流量进入 ...

  2. 使用jedis连接redis-cluster模拟缓存击穿,穿透,雪崩场景

    上一篇演示了通过redis实现ID生成器,本篇模拟缓存击穿,穿透,雪崩的场景. package com.coderman.jedis.clusterdemo.hack;import com.coder ...

  3. 《如何与面试官处朋友》系列-缓存击穿、穿透、雪崩场景原理大调解

    前面我们提到分布式多级缓存架构的全貌,但总感觉少了些什么东西.在这样大的场景下面,如果遇到缓存使用问题那可咋办?但自古英雄出少年,相信此刻你已踏马西去,正走在寻找答案上得夕阳西下.每每面谈Redis大 ...

  4. php怎么解决雪崩或穿透,Redis之缓存击穿、穿透、雪崩、预热,以及如何解决?...

    数据获取的流程,一般是前端请求,后台先从缓存中取数据,缓存取不到则去数据库中取,数据库取到了则返回给前端,然后更新缓存,如果数据库取不到则返回空数据给前端 流程图: 假如缓存的数据没有,后台则会一直请 ...

  5. Redis——缓存击穿、穿透、雪崩

    1.缓存穿透: (1)问题描述:key对应的数据并不存在,每次请求访问key时,缓存中查找不到,请求都会直接访问到数据库中去,请求量超出数据库时,便会导致数据库崩溃.如一个用户id不存在,数据库与缓存 ...

  6. 【Redis系列】缓存击穿、穿透、雪崩解决方案详解

    文章目录 前言 一.击穿 1.介绍 2.产生原因 3.解决方案 二.穿透 1.介绍 2.产生原因 3.解决方案 三.雪崩 1.介绍 2.产生原因 3.解决方案 结尾 前言 众所周知,计算机的瓶颈之一就 ...

  7. Redis缓存击穿,穿透,雪崩等问题

    雪崩(随机过期时间.永不过期). 穿透(表示恶意请求,在系统端判断是否符合规则,比如id<0,布隆过滤器). 击穿(查询加for update,永不过期) redis缓存穿透:查询一个数据库中不 ...

  8. 缓存击穿,穿透,雪崩

    一.缓存击穿 单个热点key,在不停的扛着大并发,在这个key失效的瞬间,持续的大并发请求就会击破缓存,直接请求到数据库 解决方案 使用互斥锁(Mutex Key),只让一个线程构建缓存,其他线程等待 ...

  9. redis击穿,穿透,雪崩以及解决方案

    1 击穿: 指的是单个key在缓存中查不到,去数据库查询,这样如果数据量不大或者并发不大的话是没有什么问题的. 如果数据库数据量大并且是高并发的情况下那么就可能会造成数据库压力过大而崩溃 注意: 这里 ...

  10. 谈谈redis缓存击穿透和缓存击穿的区别,雪崩效应

    谈谈redis缓存击穿透和缓存击穿的区别,雪崩效应 面试经历 在很长的一段时间里,我以为缓存击穿和缓存穿透是一个东西,直到最近去腾讯面试,面试官问我缓存击穿和穿透的区别:我回答它俩是一样的,面试官马上 ...

最新文章

  1. Day71 分页,cookie and Session
  2. 打开网站太慢linux如何检查,seo优化中网站访问慢的检测方法
  3. vs2013怎么清理解决方案_厕所漏水怎么办?厕所免拆砖防水维修方案
  4. 试编写一个将双向循环链表逆置的算法_图解:链表的快慢指针,解决 80% 的链表面试题!...
  5. php class setter,设置器 - Setters《 PHP 面向对象 》
  6. 备份文件时,添加时间戳
  7. Pr 入门教程,如何在节目监视器中修剪剪辑?
  8. Java基础梳理(一)
  9. latex图像注释位置
  10. 系统安全性之十大措施
  11. Enjoying virus ⌒●ǒ●⌒
  12. Eidetic:助你提升记忆力的酷应用
  13. 计算机重启没有用户名,电脑重启之后图标没了怎么办
  14. Safari安装使用JsonView插件
  15. android5.1 Recovery添加从U盘升级功能【转】
  16. 使用hardhat开发以太坊智能合约-测试合约
  17. bmp图片的格式详解
  18. IDEA springboot启动报错java.lang.UnsatisfiedLinkError: no tcnative-1 in java.library.path
  19. 学习大数据:Java基础篇之反射
  20. Java使用MD5加盐对密码进行加密处理,附注册和登录加密解密处理

热门文章

  1. Softmax 回归的从零开始实现 pytorch
  2. AWS DeepRacer 参数调优 Amazon SageMaker 和 Amazon RoboMaker
  3. 算法:分离链表为两部分,小于某个值都在左边,大于等于某个值在右边 Partition List
  4. 2021-09-03Dien
  5. 457.环形数组循环
  6. 输出单向链表中倒数第k个结点
  7. 名字隐藏问题(C++)
  8. 什么是document对象?如何获取文档对象上的元素?_JavaScript DOM操作元素的方法,你还记得多少?...
  9. smartMeter
  10. 一道学吧上的题目,python3 - 解决高中的古典概率问题: 有A、B两个袋子。A袋中装有4个白球、2个黑球,B袋中装有3个白球、4个黑球。从A、B两个袋子中