首页 > 数据库 >一文彻底弄懂并解决Redis的缓存雪崩,缓存击穿,缓存穿透

一文彻底弄懂并解决Redis的缓存雪崩,缓存击穿,缓存穿透

时间:2024-10-22 10:59:25浏览次数:5  
标签:缓存 请求 过期 Redis 数据库 弄懂 value key

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

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,数据库,弄懂,value,key
From: https://www.cnblogs.com/lgx211/p/18492169

相关文章

  • Redis持久化之RDB备份方式保存数据
    一:什么是redis的持久化官网介绍:英文:https://redis.io/topics/persistence中文:http://www.redis.cn/topics/persistence.html二:Redis的RDB是什么?在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里,Redis会单独创建(fork)......
  • PbootCMS缓存如何清理runtime文件夹下经常满怎么办?清理缓存的方法
    方式一:通过后台清理缓存登录后台:打开浏览器,输入你的PbootCMS后台地址,登录后台管理系统。清理缓存:登录后,在右上角找到“清理缓存”按钮,点击即可自动清理所有缓存文件。方式二:通过FTP或服务器直接删除runtime文件夹下的所有文件连接FTP服务器:使用FTP客户端(如Fil......
  • Go--获取redis/parker集群的客户端列表信息
    下载依赖:goget-ugithub.com/go-redis/redis 文件代码:packagemainimport("context""fmt""log""strings""time""github.com/go-redis/redis/v8")funcmain(){//......
  • 位置、Cookie、缓存:华为鸿蒙 ArkWeb 数据管理全攻略
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。引言ArkWeb是华为鸿蒙系统提......
  • 一文彻底弄清Redis的布隆过滤器
    布隆过滤器(BloomFilter)是一种空间效率极高的数据结构,用于快速判断一个元素是否在集合中。它能够节省大量内存,但它有一个特点:可能存在误判,即可能会认为某个元素存在于集合中,但实际上不存在;而对于不存在的元素,它保证一定不会误判。布隆过滤器适合在对存储空间要求极为严格,同时能接......
  • redis 锁的5个大坑,如何规避?
    文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录博客园版为您奉上珍贵的学习资源:免费赠送:《尼恩Java面试宝典》持续更新+史上最全+面试必备2000页+面试必备+大厂必备+涨薪必备免费赠送:《尼恩技术圣经+高并发系列PDF》,帮你实现技术自由,完成职业升级,薪......
  • pbootcms网站自动清理runtime缓存方法
    为了实现PbootCMS系统自动清理缓存目录,可以按照以下步骤操作:备份文件:在进行任何修改之前,务必备份 /apps/home/controller/ExtLabelController.php 文件,以防出现意外情况。修改 ExtLabelController.php 文件:打开 /apps/home/controller/ExtLabelController.php ......
  • RockyLinux安装redis
    本文介绍RockyLinux使用dnf在线安装redis并修改密码设置远程登陆。本博客使用RetHat系的新版本系统,如使用Debian系的系统如Ubuntu,只需使用apt安装,其余部分类似。1、使用如下命令安装redissudodnfinstallredis-server2、安装完成后可以使用systemctl工具对redis服务进行控......
  • 滚雪球学Redis[9.1讲]:Redis常见问题排查指南:解决错误、优化性能与确保数据一致性
    全文目录:......
  • 滚雪球学Redis[8.2讲]:Redis的未来发展趋势:从云服务到AI与物联网的前沿探索
    全文目录:......