Redis 缓存穿透及缓存雪崩

本文介绍了缓存系统中的三种常见问题:缓存穿透是大量请求不存在的数据,导致数据库压力增大;缓存击穿是热点key失效时,请求直接落到数据库;缓存雪崩则是多个热点key同时过期,引发数据库崩溃。针对这些问题,提出了缓存不存在的key、使用布隆过滤器、设置key的随机过期时间等解决方案。

在学习缓存穿透和缓存雪崩之前,先来了解以下关于缓存命中的概念

目录

一. 缓存穿透

二. 缓存击穿

三. 缓存雪崩  


缓存命中:指数据在缓存中查询到,不用再访问 mysql 数据库

一. 缓存穿透

       用户或者攻击者大量访问不存在于缓存和 mysql 中的数据,此时缓存被穿透失去作用,当高并发或有人利用不存在的 Key 频繁攻击时,底层数据库的压力骤增,甚至崩溃

模拟缓存穿透

  ProductController 类和 JedisUtil 类在上一篇博客中

public class TestCachePenetration {
    public static void main(String[] args) {
        ProductController product = new ProductController();
        // 大量请求查询不存在 redis 和 mysql 的数据
        for (int i = 0; i<100; i++){
            product.getProductById(-1);
        }
    }
}

  解决办法1:缓存不存在的 key

@RestController
public class ProductController {
    @Resource
    ProductMapper productMapper;
    @GetMapping("/getProductById")
    public Product getProductById(int id){
        Product product = null;
        // 1. 查看 redis 缓存中是否有数据
        product = JedisUtil.getProductByRedis(id);
        if(product == null){  // redis 中没有该商品
            System.out.println("redis中没有该商品");
            // 进 mysql 中查询
            product = productMapper.getProductById(id);
            if(product==null){ // mysql 中没有
                System.out.println("mysql 中未查询到该商品");
                // 缓存穿透---创建一个不存在的key
                Product p = new Product(id," ", 0.0F,0);
                JedisUtil.saveToRedis(p);
            }else { // mysql 中有
                System.out.println("mysql: "+product);
                // 返回给前端的同时也要将数据写入到 redis 中
                long flag = JedisUtil.saveToRedis(product);
                System.out.println("save flag:"+flag);
            }
        }else {  // redis 中有该商品
            System.out.println("redis: " + product);
        }
        return product;
    }
}

解决办法2:布隆过滤器 

用途:布隆过滤器一般用于在大数据量集合中判定某个元素是否存在

特点

  1.   布隆过滤器判断存在,只是可能存在,不一定真的存在
  2.   布隆过滤器判断不存在,则一定不存在

原理:当某个 key 被加入集合时,通过 k 个 hash 函数将这个 key 映射成到位数组中的 k 个点,将  这些点置为 1。当查询数据时,先进布隆过滤器查询,将查询元素通过 hash 函数映射到 k 个点,如果这些点全是 1,则该元素可能存在,进 redis 缓存或者 mysql 数据库查询;若这些点有任何一个是 0,则该元素一定不存在,直接返回,无需再查询缓存和数据库


二. 缓存击穿

       当某个热点key失效,短时间内大量高并发对该key的请求就会穿过redis缓存,到达底层mysql数据库,短时间内导致数据库压力骤增,数据库崩溃

解决方法:

  1. 对于某个热点key,可以设置某段时间内永不过期,防止失效产生缓存击穿
  2. 加互斥锁,当多个请求到达底层mysql数据库时,给请求线程加互斥锁,当第一个线程拿到数据后,会将数据写入到缓存中,这样其他等待的请求就可以从缓存中直接读取数据

三. 缓存雪崩  

       当大量热点 key 被同时缓存进 redis 中,就会导致某个时刻这些热点 key 同时过期,此时大量高并发的查询热点 key 的请求被转发到底层 mysql 数据库,从而导致数据库崩溃,甚至宕机

与缓存击穿区别:

  1. 缓存击穿是指单个key过期,大量请求穿过缓存到达底层数据库
  2. 缓存雪崩是成片的,指多个key同时失效,导致大量高并发请求

 解决办法1:给 key 设置生命时间时添加一个随机值

    // 存储商品到 redis---key:(product:id)  field:  value
    public static long saveToRedis(Product product){
        Jedis jedis = JedisUtil.getJedisPool();

        HashMap<String,String> hashMap = new HashMap();
        hashMap.put("name",product.getName());
        hashMap.put("price",String.valueOf(product.getPrice()));
        hashMap.put("category",String.valueOf(product.getCategory()));

        String key = "product:" + product.getId();
        long flag = jedis.hset(key, hashMap);
        // 生成一个 [0,100) 左闭右开的随机数
        int random = new Random().nextInt(100);
        jedis.expire(key,3600L+random);

        jedis.close();
        return flag;
    }

 解决办法2:

       如果是因为Redis服务器宕机,导致缓存失效,产生的缓存雪崩,可以通过构建主从复制,哨兵机制,cluster集群模式,来防止因为redis服务器故障产生的雪崩问题 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值