当前位置: 首页 > article >正文

【Redis】缓存击穿与缓存雪崩:问题与解决方案

前言

用户数据通常存储在数据库中,而数据库的数据则保存在磁盘上。由于磁盘的读写很慢,为了避免用户直接访问数据库,我们可以使用 Redis 作为缓存层。而引入缓存层后,会出现三种缓存异常问题:缓存穿透缓存击穿缓存雪崩,这篇文章主要探讨缓存击穿缓存雪崩的问题。

想要了解缓存穿透及其解决方法,可以看我的这篇博客:【Redis】缓存穿透详解


1.缓存击穿

1.1 什么是缓存击穿

缓存击穿是指在缓存中没有命中数据(缓存不存在),并且该数据正好是一个热点数据(经常访问的、被频繁请求的数据)。由于缓存中没有该数据,系统会直接访问数据库,而在高并发的情况下,多个请求会同时穿透缓存,直接访问数据库,造成数据库压力骤增,可能导致数据库崩溃或系统性能下降。


1.2 缓存击穿的产生原因

缓存击穿通常发生在以下几种情况:

  • 热点数据的缓存失效:当某个热点数据的缓存过期时,大量请求涌入到数据库层,而此时数据库需要处理所有的请求,造成数据库的瞬时压力增大。
  • 缓存中没有数据:某些数据本身并没有被缓存,可能是由于缓存策略不当、数据没有被及时缓存,或者缓存过期。

1.3 如何解决缓存击穿

缓存击穿的核心问题是如何有效避免缓存失效后,多个请求直接击穿缓存访问数据库。

1.3.1 热点数据永不过期

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


1.3.2 使用互斥锁(锁机制)

在缓存失效的情况下,采用分布式锁或互斥锁来确保只有一个线程/请求去加载数据库数据,并更新缓存。其他线程则等待,直到缓存被更新完毕。例如,可以通过 Redis 的 SETNX(set if not exists)命令来实现分布式锁。

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);
    }
}

1.3.3 预防性缓存更新

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


2.缓存雪崩

2.1 什么是缓存雪崩

缓存雪崩是指缓存中的大量数据在短时间内同时过期,导致大量请求同时访问数据库,造成数据库的瞬间负载激增,可能导致数据库崩溃。缓存雪崩的问题通常发生在缓存中的数据都在同一时刻过期,尤其是缓存的过期时间相同或过期时间比较集中时。


2.2 缓存雪崩的产生原因

缓存雪崩通常是以下几种原因导致的:

  • 缓存过期时间设计不当:多个缓存数据设置了相同的过期时间,导致它们在同一时刻过期,造成大量请求同时访问数据库。
  • 缓存服务器宕机或故障:缓存服务器出现故障,所有缓存数据无法提供服务,导致请求直接访问数据库。
  • 缓存容量不足:缓存空间不足,无法承载大量的数据,导致频繁失效和大量缓存穿透。

2.3 如何解决缓存雪崩

解决缓存雪崩的根本方法是避免大量缓存数据同时失效,或者通过其他手段避免请求直接访问数据库

2.3.1 设置不同的缓存过期时间

为避免缓存雪崩,最直接的解决办法是让缓存中的不同数据拥有不同的过期时间。可以为每个缓存项添加一定的随机值,避免缓存的失效时间集中在某一时刻。

// 为每个缓存设置一个不同的过期时间,带有随机偏移量
long randomOffset = ThreadLocalRandom.current().nextLong(0, 60000);
redisTemplate.opsForValue().set(cacheKey, value, baseExpireTime + randomOffset, TimeUnit.MILLISECONDS);

2.3.2 使用缓存预热

在系统启动时,或者通过某种定时机制对缓存进行预热。即提前加载一些关键数据到缓存中,避免在数据首次请求时缓存为空,从而导致直接访问数据库。


2.3.3 降级策略

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


http://www.kler.cn/a/386797.html

相关文章:

  • 【缺陷检测】Anomaly Detection via Reverse Distillation from One-Class Embedding
  • Zookeeper的安装与使用
  • Mysql数据库里的SSH连接
  • Qt 和 WPF(Windows Presentation Foundation)
  • quartz
  • css:盒子模型
  • 网络原理(应用层)->HTTP
  • ArcGIS Pro SDK Addin-DAML
  • 十六 MyBatis使用PageHelper
  • 【韩老师零基础30天学会Java 】03章 变量
  • selenium大量并发连接驱动超时
  • sealos部署K8s,安装docker时master节点突然NotReady
  • # SpringCloud学习
  • 虚拟现实辅助工程技术如何加速汽车设计与制造
  • 接口类和抽象类在设计模式中的一些应用
  • 深度学习:bert模型
  • Nuxt.js 应用中的 prepare:types 事件钩子详解
  • MySQL 四类管理日志
  • 力扣(leetcode)每日一题 3255 长度为 K 的子数组的能量值 II|滑动窗口
  • 【系统架构设计师】2022年真题论文: 论软件维护方法及其应用(包括解题思路和素材)
  • 决策树(部分)
  • Ubuntu 22 安装 Apache Doris 3.0.3 笔记
  • (十五)JavaWeb后端开发——异常处理/AOP面向切面编程
  • 六、SpringMVC的视图
  • C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
  • React 入门课程 - 使用CDN编程React