缓存雪崩、缓存击穿、缓存穿透是分布式系统中使用缓存时,常遇到的三类问题,都会对系统性能和稳定性产生严重影响。下面将详细介绍这三者的定义、产生原因、危害以及常见的解决方案。

1. 缓存雪崩

1.1 定义

缓存雪崩是指在某一时刻,大量缓存同时失效,导致大量请求直接打到数据库层,造成数据库压力骤增,甚至可能导致数据库崩溃、系统不可用的情况。

1.2 产生原因

  • 缓存集中失效:通常情况下,缓存的失效时间(TTL)是设置好的,但如果大量缓存键设定了相同或接近的过期时间点,那么在这些缓存集中失效时,会造成大量的请求无法从缓存中读取数据,只能直接访问数据库。
  • 缓存服务器宕机:如果 Redis 服务器集群出现宕机或故障,那么所有缓存数据会瞬间不可用,大量请求直接涌向数据库。

1.3 危害

  • 数据库压力激增:大量并发请求瞬间打到数据库,可能造成数据库连接数耗尽、性能下降,甚至宕机。
  • 服务不可用:由于数据库无法及时响应请求,系统整体响应速度变慢或完全失去响应,导致服务不可用。

1.4 解决方案

  • 缓存过期时间分散化

    • 可以为不同的缓存键设置不同的失效时间(TTL),使得缓存的过期时间均匀分布,避免大量缓存同时失效。例如,在设定 TTL 时,加上一个随机值,避免缓存键在同一时间失效。
    // 设置缓存时,加一个随机时间,防止集中过期
    int randomTTL = ttl + new Random().nextInt(100);
    redisTemplate.opsForValue().set(key, value, randomTTL, TimeUnit.SECONDS);
  • 缓存预热

    • 在系统上线前,提前将热点数据加载到缓存中,避免大量请求同时触发缓存未命中的情况。
  • 降级策略

    • 在缓存雪崩时,可以采取限流、降级等策略,减缓数据库的压力。如在缓存失效时,直接返回默认值或缓存过期的旧数据,避免数据库短时间内处理大量请求。
  • 多级缓存架构

    • 使用本地缓存(如 Caffeine、Guava 等)和分布式缓存(如 Redis)相结合的方式,部分热点数据可以先放入本地缓存,降低 Redis 和数据库的压力。
  • Redis 高可用

    • 部署 Redis 主从集群,使用 Redis 的哨兵模式(Sentinel)或者 Redis Cluster 来实现高可用,避免缓存服务器单点故障。

2. 缓存击穿

2.1 定义

缓存击穿是指缓存中存储的某个热点数据在某一时刻失效,大量并发请求同时去访问这个热点数据,导致所有请求打到数据库,造成数据库压力骤增的情况。

2.2 产生原因

  • 热点缓存失效:当某个热点数据的缓存过期时,大量请求涌入到数据库层,而此时数据库需要处理所有的请求,造成数据库的瞬时压力增大。

2.3 危害

  • 数据库压力过大:由于热点数据失效,导致瞬间的大量请求直接打到数据库,增加数据库的压力,可能会引发数据库连接耗尽、响应变慢等问题,严重时可能导致数据库宕机。

2.4 解决方案

  • 热点数据永不过期

    • 对于特别重要的热点数据,可以考虑不设置缓存过期时间,让这些数据一直保存在缓存中。可以通过定时任务手动更新缓存中的数据来避免数据过期问题。
  • 互斥锁(Mutex)机制

    • 为了解决在缓存失效瞬间,大量请求同时访问数据库的问题,可以通过加锁机制,保证同一时刻只有一个线程能访问数据库。其他线程需要等待该线程将新数据写入缓存后,再读取缓存。
    String value = redisTemplate.opsForValue().get(key);
    if (value == null) {
    // 获取分布式锁
    if (redisTemplate.opsForValue().setIfAbsent(lockKey, "lock", 10, TimeUnit.SECONDS)) {
    try {
    // Double-check
    value = redisTemplate.opsForValue().get(key);
    if (value == null) {
    // 查询数据库
    value = database.get(key);
    // 将结果写入缓存
    redisTemplate.opsForValue().set(key, value, ttl, TimeUnit.SECONDS);
    }
    } finally {
    // 释放锁
    redisTemplate.delete(lockKey);
    }
    } else {
    // 等待锁释放后,再从缓存中读取数据
    Thread.sleep(100); // 自行调整等待时间
    value = redisTemplate.opsForValue().get(key);
    }
    }
  • 预防性缓存更新

    • 在热点数据即将过期时,提前异步刷新缓存。通过检测热点数据的访问频率,当即将过期时触发自动更新操作,避免过期瞬间的击穿问题。
  • 双缓存机制

    • 可以采用双层缓存策略:一个主要缓存层负责缓存大部分数据,另一个次缓存层保存上次的缓存数据。在主要缓存失效时,可以直接从次缓存层读取数据,避免直接打到数据库。

3. 缓存穿透

3.1 定义

缓存穿透是指恶意用户或程序请求查询的数据在缓存和数据库中都不存在,导致每次请求都会直接打到数据库,绕过缓存。由于缓存没有存储该请求的结果,所有这类请求都会绕过缓存,直接访问数据库,从而导致数据库承受巨大的压力。

3.2 产生原因

  • 恶意攻击:有意构造大量不存在的数据请求,如查询不存在的用户 ID 或商品 ID,缓存中没有这些数据,因此直接请求数据库。
  • 查询不存在的键:一些业务逻辑上无法避免查询不存在的数据,例如用户查询某些过时或错误的请求参数,数据库中也没有相应的记录。

3.3 危害

  • 数据库性能下降:由于查询的数据既不在缓存中,也不在数据库中,因此每次请求都会直接打到数据库,造成数据库压力增大,甚至引发性能瓶颈。

3.4 解决方案

  • 缓存空结果

    • 如果查询的某个键在数据库中不存在,则将该键的查询结果(如 null 或空值)缓存起来,并设定一个较短的过期时间,防止该键反复查询打到数据库。
    // 查询缓存
    String value = redisTemplate.opsForValue().get(key);
    if (value == null) {
    // 查询数据库
    value = database.get(key);
    if (value == null) {
    // 缓存空结果,避免缓存穿透
    redisTemplate.opsForValue().set(key, "null", 5, TimeUnit.MINUTES);
    } else {
    // 将数据库中的值写入缓存
    redisTemplate.opsForValue().set(key, value, ttl, TimeUnit.SECONDS);
    }
    }
  • 布隆过滤器(Bloom Filter)

    • 使用布隆过滤器对所有可能存在的数据进行标记,所有请求先经过布隆过滤器进行校验,只有布隆过滤器认为存在的数据,才会去查询缓存或数据库。这样可以有效拦截掉绝大多数不存在的请求,防止这些请求绕过缓存直接打到数据库。
    BloomFilter bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("UTF-8")), 100000);
    
    // 将所有可能的合法键加入布隆过滤器
    bloomFilter.put("validKey1");
    bloomFilter.put("validKey2"); // 查询时先校验布隆过滤器
    if (!bloomFilter.mightContain(key)) {
    return "Invalid Key";
    }
    // 正常查询缓存和数据库
  • 参数校验

    • 在查询请求进入系统前,进行严格的参数校验和过滤,避免不合法的请求进入系统。例如用户 ID 或商品 ID 是否符合格式要求,避免恶意构造的非法请求直接打到数据库。

一文彻底弄懂并解决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缓存中进行处理而被发送到数据库,导致数据库压力陡增. ...

  6. redis详解及应用(雪崩、击穿、穿透)

    一. redis的简介与安装 引用:https://www.cnblogs.com/ysocean/tag/Redis%E8%AF%A6%E8%A7%A3/ 二. redis的配置文件介绍 引用:ht ...

  7. redis雪崩,击穿,穿透

    redis穿透 什么是redis穿透? 1.查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存 2.这将导致这个不存在的数据每次请求都要到存储层 ...

  8. NoSQL:Redis缓存、雪崩、击穿、穿透

    Redis介绍 Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库 ...

  9. 一文彻底弄懂cookie、session、token

    前言 作为一个JAVA开发,之前有好几次出去面试,面试官都问我,JAVAWeb掌握的怎么样,我当时就不知道怎么回答,Web,日常开发中用的是什么?今天我们来说说JAVAWeb最应该掌握的三个内容. 发 ...

  10. 一文彻底弄懂this关键字用法

    哈喽,大家好,我是指北君. 介绍完 native.static.final 关键字后,指北君再接再厉,接着为大家介绍另一个常用的关键字--this. this 也是Java中的一个关键字,在<J ...

随机推荐

  1. 【转载】 推荐系统 EE 问题与 Bandit 算法

    原文地址: https://toutiao.io/posts/584etm/preview ------------------------------------------------------ ...

  2. 多线程之深入理解park与unpark

    1.背景 面试官问,如何暂停一个线程勒..... 说说你对park的理解....... 2.代码 package com.ldp.demo01; import com.common.MyThreadU ...

  3. [CEOI2007] 树的匹配 Treasury 题解

    前言 题目链接:洛谷. 题目简述 给一棵树,问你这棵树的最大匹配是多少,并且计算出有多少种最大匹配. 题目分析 先来考虑较简单的最大匹配数.对于某一个结点,它有以下三种状态: 不参与匹配: 和某一个儿 ...

  4. games101 作业1及作业2分析及解决

    games101 作业1及作业2分析及解决 去年的时候把games101的课程以及作业完成,但是整个过程比较粗略,也借助了不少外界的力量(doge),于是最近准备抽几天集中再把作业(1-7)过一遍,常 ...

  5. 高阶函数之reduce

    let arrone = [1, 2, 3, 4] let arrtwo = [{ age: 1 }, { age: 2 }, { age: 3 }] let arrthree = [[1, 2, 3 ...

  6. 10-canva绘制数据点

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...

  7. quartz监控日志(一)

    最近几个月,现网总是出现定时器不执行的情况,或者定时器卡死的情况,而又不方便排查,只能依靠quartz的debug日志以及错误日志来监控定时器的执行情况,并且随着我们系统中job越来越多,而使得job ...

  8. plupload附件上传插件IE8问题

    前段时间遇到一个plupload上传插件问题,在其他浏览器上面运行很正常,但是就是在IE8上面第一次点击上传按钮无反应,后面再连续点击才ok.我的初始化代码如下 _this.uploader = ne ...

  9. jxls导入excel

    我们在开发中经常用jxls实现导出功能,殊不知jxls也有导入功能,下面来介绍下如何使用jxls导入excel. 首先在maven的pom中添加相关依赖,如下: <dependency> ...

  10. 9. 从0学ARM Cortex-A9 LED汇编、C语言驱动编写

    0. 前言 一般我们购买一个开发板,厂家都会给出对应的电路图文件,我们可以通过搜索对应名称来查找到对应的外设.对于驱动工程师来说,我们只需要知道外设与SOC交互的一些数据线和信号线即可. 用主控芯片控 ...