高并发下的缓存失效问题


缓存击穿

    1.   对于一些设置了过期时间的 key, 如果这些 key 可能还在某些时间点被超高并发地访问,是一种非常热点的数据(比如: 某某明星吃瓜事件等)
    2.   如果这个 key 在大量请求进来时正好失效,那么这些 key 就会穿过缓存区访问数据库
    3.        解决方法:加并发锁,在大量并发请求下,只让一个人去查,而其他人进行等待,查到之后释放锁,其他人再去获取锁,先查缓存,就会有数据,不用去 db
    4. 在单体应用中加锁,使用 synchronized
sychronized(this){
        String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");
        if(!StringUtils.isEmpty(catalogJSON)){
          // 加锁后,再次查询缓存
          // 如果缓存不为 null, 直接返回
        Map> result = JSON.parseObject(catalogJSON, new TypeReference>(){});
// 查到的结果,应该立即放入缓存,以防锁释放后,其他线程获取锁,但是从缓存中还没读到数据,又会再次查询数据库,导致的时序问题带来的查了两次数据库问题
String s = JSON.toJSONString(catalogJsonFromDb);
redisTemplate.opsForValue().set("catalogJSON", s, 1, TimeUnit.DAYS);
return result;   }

缓存穿透

    1.   在缓存中查询一个不存在的数据,由于缓存没有被命中,就会去查询数据库,数据库中也不存在此条记录,那么每次的请求都会越过缓存区查询数据库,缓存就失去了作为缓存的意义。
    2.         带来的问题:
      1.   当请求并发量很大的时候,数据库的压力增大的,最终可能导致崩溃
    3.         解决方法:
      1.  将该查询的key ,给它一个 value 为 null 的值,并加入短暂的过期时间,以防后面数据库有该值了,这个数据一直占着。
      2. // 从缓存中获取数据
        String catalogJson = redisTemplate.opsForValue().get("catalogJSON");
        // 判断数据是否是空的
        if(StringUtils.isEmpty(catalogJSON)){
        // 如果为空,那么查询数据库
        Map> catalogJsonFromDb = getCatalogJsonFromDb();
        // 将查到的数据再放入缓存,将对象转为 json 放在缓存中
        String s = JSON.toJSONString(catalogJsonFromDb);
        redisTemplate.opsForValue().set("catalogJSON", 0);
        return catalogJsonFromDb;
        }
        

缓存雪崩

    1.   设置缓存时 key 采用相同的过期时间,导致缓存在某一时刻同时失效,请求因此进入数据库,导致数据库压力过大。
    2.   解决方法: 设置的过期时间尽量不要一样,可以在一个基础的失效时间上增加一个随机值(1-5 分钟左右), 这样可以有效避免缓存大量失效。
    3. // 从缓存中获取数据
      String catalogJson = redisTemplate.opsForValue().get("catalogJSON");
      // 判断数据是否是空的
      if(StringUtils.isEmpty(catalogJSON)){
      // 如果为空,那么查询数据库
      Map> catalogJsonFromDb = getCatalogJsonFromDb();
      // 将查到的数据再放入缓存,将对象转为 json 放在缓存中
      String s = JSON.toJSONString(catalogJsonFromDb); 
      redisTemplate.opsForValue().set("catalogJSON", s, 1, TimeUnit,DAYS); // 时间按实际需要定义
      return catalogJsonFromDb;
      }