Redis中的缓存击穿、穿透、雪崩

  • 缓存击穿:指某个热点数据在缓存中失效,导致大量请求直接访问数据库。此时,由于瞬间的高并发,可能导致数据库崩溃
  • 缓存穿透:指查询一个不在的数据,缓存中没有相应的记录,每次请求都会去数据库查询,造成数据库负担加重
  • 缓存雪崩:指多个缓存数据在同一时间过期,导致大量请求同时访问数据库,从而造成数据库瞬间负载激增

解决方案

  • 缓存击穿

    • 使用互斥锁,确保同一时间只有一个请求可以去数据库中查询并更新缓存
    • 热点数据永不过期
  • 缓存穿透

    • 使用布隆过滤器,过滤掉不存在的请求,避免直接访问数据库
    • 对查询结果进行缓存,即使是不存在的数据,也可以缓存一个标识,以减少对数据库的请求
  • 缓存雪崩

    • 采用随机过期时间策略,避免多个数据同时过期
    • 使用双缓存策略,将数据同时存储在两层缓存中,减少数据库直接请求

缓存击穿

击穿.png

缓存击穿指的是某一热点数据缓存失效,使得大量请求直接打到了数据库,增加数据库负载

场景:大家在网购平台抢购茅台,但在某一时刻茅台的缓存失效了,所有的请求全部打到了数据库,这就是缓存击穿

互斥锁实现

public class CacheService {
    private Jedis jedis = new Jedis("localhost");
    private Lock lock = new ReentrantLock();
​
    public String getData(String key) {
        // 尝试从缓存获取数据
        String value = jedis.get(key);
        
        // 如果缓存不存在
        if (value == null) {
            // 加锁以防止并发请求
            lock.lock();
            try {
                // 再次检查缓存,避免重复查询
                value = jedis.get(key);
                if (value == null) {
                    // 查询数据库
                    value = queryDatabase(key);
                    // 将结果放入缓存
                    jedis.set(key, value);
                }
            } finally {
                // 释放锁
                lock.unlock();
            }
        }
        return value;
    }
}
​

说明

  • 缓存查询:先尝试从Redis中获取数据
  • 加锁:如果缓存中没有数据,使用ReentrantLock加锁,确保只有一个线程可以查询数据库
  • 二级检查:在加锁后再次检查缓存,避免重复查询
  • 数据库查询:如果缓存仍没有数据,查询数据库并缓存起来
  • 释放锁:确保锁在查询结束后被释放,防止死锁

缓存穿透

穿透.png

缓存穿透是指一个不存在的数据,由于缓存中不存在,导致每次请求都直接访问数据库,增加数据库负载

攻击者可以通过构造不存在的key发起大量请求,对数据库造成很大的压力,可能会造成系统宕机

解决方案

  • 防止非法请求:检查非法请求,封禁其IP以及账号
  • 缓存空值:将数据库中不存在的结果也缓存起来,并设置一个较短的过期时间,避免频繁查询数据库
  • 使用布隆过滤器:使用布隆过滤器来快速判断一个请求的数据是否存在,如果布隆过滤器判断数据不存在,则直接返回,避免查询数据库

缓存雪崩

雪崩.png

缓存雪崩是指在某个时间点,大量缓存同时失效或被清空,导致大量请求直接打到了数据库,造成负载,甚至引发系统崩溃

解决方案

  • 过期时间随机化:设置缓存过期时间,加上一个随机值,避免同一时间大量缓存失效
  • 使用多级缓存:引入多级缓存机制,如本地缓存和分布式缓存相结合,减少单点故障风险
  • 缓存预热:系统启动时提前加载缓存数据,避免大量请求落到冷启动状态下的数据库
  • 加锁互斥:使得没有缓存或缓存失效的情况下,同一时间只有一个请求访问数据库
最后修改:2025 年 03 月 26 日
如果觉得我的文章对你有用,请随意赞赏