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

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

缓存击穿(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. 空值缓存的过期时间不宜过长,防止内存浪费。
相关推荐
暴躁小师兄数据学院12 小时前
【AI大数据工程师特训笔记】第10讲:数据库用户、权限管理、数据库约束
大数据·数据库·笔记·sql·postgresql
凤山老林12 小时前
DDD(领域驱动设计)在复杂业务系统中的落地指南
java·开发语言·数据库·ddd·领域驱动
凯瑟琳.奥古斯特12 小时前
子查询原理与实战案例解析
开发语言·数据库·职场和发展·数据库开发
自传.12 小时前
Redis 高频考试面试知识点1
redis·aof·rdb·redis面试
KaMeidebaby12 小时前
卡梅德生物技术快报|酵母双杂交 cDNA 文库构建与蛋白互作筛选流程
服务器·前端·数据库·人工智能·算法
暴躁小师兄数据学院12 小时前
【AI大数据工程师特训笔记】第02讲:PostgreSQL数据库生态全景
大数据·数据库·人工智能·postgresql
沐风___12 小时前
App 上架之后:如何看数据、获取用户与持续迭代产品
服务器·前端·数据库
夜微凉413 小时前
三、MySQL
android·数据库·mysql
小新同学^O^13 小时前
Redis的简单总结
数据库·redis·学习
暴躁小师兄数据学院13 小时前
【AI大数据工程师特训笔记】第11讲:正则表达式与正则函数
数据库·mysql