缓存 --- 缓存击穿, 缓存雪崩, 缓存穿透

缓存 --- 缓存击穿, 缓存雪崩, 缓存穿透

缓存击穿(Cache Breakdown)

概念原理

  • 定义 :某个 热点数据(高频访问的 Key) 在缓存中过期时,瞬间有大量并发请求涌入,直接穿透缓存访问数据库,导致数据库压力骤增。
  • 核心原因:高并发场景下,热点数据缓存失效的瞬间,请求集中访问数据库。
  • 本质问题:缓存失效时间的集中性与高并发请求的瞬时性冲突。

实际场景

  • 电商秒杀:某商品参与限时秒杀活动,缓存设置为 10 分钟过期。活动开始后缓存突然失效,瞬间数万请求直接访问数据库查询库存。

代码实现(互斥锁方案)

java 复制代码
public String getProduct(String productId) {
    String cacheKey = "product:" + productId;
    // 1. 从缓存获取数据
    String product = redisTemplate.opsForValue().get(cacheKey);
    if (product != null) {
        return product;
    }
    
    // 2. 缓存未命中,尝试获取分布式锁
    String lockKey = "lock:product:" + productId;
    boolean locked = redisTemplate.opsForValue().setIfAbsent(
        lockKey, "locked", 30, TimeUnit.SECONDS
    );
    
    try {
        if (locked) {
            // 3. 获取锁成功,查询数据库
            product = productService.getById(productId);
            // 4. 回写缓存并设置过期时间
            redisTemplate.opsForValue().set(cacheKey, product, 1, TimeUnit.HOURS);
        } else {
            // 5. 未获取锁,等待后重试(避免循环等待需设置最大重试次数)
            Thread.sleep(100);
            return getProduct(productId); // 递归重试
        }
    } finally {
        // 6. 释放锁
        if (locked) {
            redisTemplate.delete(lockKey);
        }
    }
    return product;
}

关键点

  • 使用 setIfAbsent 实现分布式锁,避免多个线程同时重建缓存。
  • 锁需设置超时时间(如 30 秒),防止死锁。
  • 未获取锁的线程需等待后重试(或返回默认值)。

缓存雪崩(Cache Avalanche)

概念原理

  • 定义 :大量缓存数据 在同一时间过期,导致所有请求直接访问数据库,引发数据库崩溃。
  • 核心原因:缓存过期时间设置过于集中。
  • 本质问题:缓存失效的"雪崩效应"导致数据库瞬时压力过大。

实际场景

  • 电商大促:所有商品缓存设置为凌晨 0 点过期,导致瞬时大量商品详情查询请求直接压垮数据库。

代码实现(随机过期时间)

java 复制代码
public void setProductCache(String productId, Product product) {
    String cacheKey = "product:" + productId;
    // 基础过期时间 + 随机偏移量(0~300秒)
    int baseExpire = 3600; // 1小时
    int randomExpire = new Random().nextInt(300); 
    redisTemplate.opsForValue().set(
        cacheKey, 
        product, 
        baseExpire + randomExpire, 
        TimeUnit.SECONDS
    );
}

// 查询时统一使用随机过期时间
public Product getProduct(String productId) {
    String cacheKey = "product:" + productId;
    Product product = redisTemplate.opsForValue().get(cacheKey);
    if (product == null) {
        product = productService.getById(productId);
        setProductCache(productId, product); // 设置缓存时添加随机过期时间
    }
    return product;
}

关键点

  • 在基础过期时间上添加随机偏移量(如 0~300 秒),分散缓存过期时间。
  • 结合 Redis 集群部署,提升缓存层的高可用性。

缓存穿透(Cache Penetration)

概念原理

  • 定义 :大量请求查询 数据库中不存在的数据,缓存无法命中,导致请求直接穿透到数据库。
  • 核心原因:恶意攻击或业务逻辑漏洞(如查询无效 ID)。
  • 本质问题:缓存层和数据库层均无法拦截无效请求。

实际场景

  • 恶意爬虫攻击 :攻击者批量请求 -10 或超大数值的用户 ID,绕过缓存直接查询数据库。

代码实现(布隆过滤器 + 空值缓存)

java 复制代码
// 使用 Guava 布隆过滤器(初始化时预热合法ID)
private BloomFilter<String> bloomFilter = BloomFilter.create(
    Funnels.stringFunnel(StandardCharsets.UTF_8), 1000000, 0.01
);

public User getUser(String userId) {
    // 1. 布隆过滤器拦截非法请求
    if (!bloomFilter.mightContain(userId)) {
        return null;
    }
    
    String cacheKey = "user:" + userId;
    User user = redisTemplate.opsForValue().get(cacheKey);
    if (user != null) {
        return "NULL".equals(user) ? null : user;
    }
    
    // 2. 查询数据库
    user = userService.getById(userId);
    if (user == null) {
        // 3. 缓存空值(防止反复穿透)
        redisTemplate.opsForValue().set(cacheKey, "NULL", 5, TimeUnit.MINUTES);
        return null;
    }
    
    // 4. 回写缓存
    redisTemplate.opsForValue().set(cacheKey, user, 1, TimeUnit.HOURS);
    return user;
}

关键点

  • 布隆过滤器初始化时需预热合法 ID(如从数据库加载)。
  • 缓存空值(如 "NULL")并设置短过期时间(如 5 分钟)。
  • 布隆过滤器存在误判率(可配置 0.01),但不会漏判。

总结对比

问题类型 触发条件 解决方案 核心实现技术
击穿 热点Key过期 + 高并发 互斥锁、逻辑过期 分布式锁(SETNX)、异步重建
穿透 查询不存在的数据 布隆过滤器、空值缓存 Bloom Filter、NULL占位
雪崩 大量Key同时过期 随机过期时间、集群容灾 过期时间随机化、Redis集群

注意事项

  1. 布隆过滤器需要预热数据,适用于静态数据(如商品ID列表)。
  2. 互斥锁需设置合理的锁超时时间,避免锁过期后业务线程仍在执行。
  3. 空值缓存的过期时间不宜过长,防止内存浪费。
相关推荐
风哥2号2 小时前
数据库教程FGMT27‑MySQL数据库基础知识与体系架构
数据库·mysql
YangYang9YangYan2 小时前
2026 校招商品分析岗位 JD 拆解,核心指标、工具与面试考点
大数据·数据库·数据分析
西安栈上月明软件科技3 小时前
从业务黑话到本体图谱:OAG本体建模五步法(西安老系统AI化改造实战)
数据库·人工智能·架构
anxiao_m3 小时前
跨云跨机房大数据迁移怎么选?不同场景工具适配指南
数据库·云启快传
wdfk_prog4 小时前
ROS教程:01 搭建 ROS1 Noetic Docker 开发环境
运维·缓存·docker·容器·ros
wdfk_prog5 小时前
ros教程02:创建 catkin Workspace、Package 与第一个 ROS1 C++ Node
运维·缓存·docker·容器·ros
夕除5 小时前
redis--015
redis
风哥2号5 小时前
数据库教程FGMT43‑MySQL性能分析与优化调整
数据库·mysql
随身数智备忘录6 小时前
财务数据分析如何与业务场景结合?财务数据分析如何从数出有据到数出有用?
数据库
这个DBA有点耶7 小时前
数据库教程:从零基础到实战的完整学习路径(2026版)
数据库·程序员·代码规范