认识常见中间件-redis(二)-redis缓存雪崩、缓存击穿、缓存穿透

缓存雪崩

  缓存雪崩指的是大量的请求无法在 Redis 缓存系统中处理,请求全部打到数据库,导致数据库压力激增,甚至宕机。出现该错误的原因主要有两种:

  • 大量热点数据同时过期,导致大量请求需要查询数据库并写到缓存;
  • Redis 故障宕机,缓存系统异常。

  对于缓存雪崩可以采取的方案主要有两种:过期时间添加随机值、接口限流

  • 过期时间添加随机值:要避免给大量的数据设置一样的过期时间,过期时间 = baes 时间+ 随机时间(较小的随机数,比如随机增加 1~5 分钟)。还有一种更激进的方法就是干脆设置缓存不过期,通过后台服务更新缓存。
  • 接口限流:前端限制接口访问频次

缓存击穿(单一热点数据、高并发、数据失效)

  如果缓存中的某个热点数据过期了,此时大量的请求访问了该热点数据,就无法从缓存中读取,直接访问数据库,数据库很容易就被高并发的请求冲垮,这就是缓存击穿的问题。
  可以发现缓存击穿跟缓存雪崩很相似,可以认为缓存击穿是缓存雪崩的一个子集。 应对缓存击穿可以采取以下方案:

  1. 过期时间添加随机值或者热点数据干脆不设置过期时间
  2. 使用互斥锁方案(Redis 中使用 setNX 方法设置一个状态位,表示这是一种锁定状态),保证同一时间只有一个业务线程请求缓存,未能获取互斥锁的请求,要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。
public Object getData(String id) {
    
    
    String desc = redis.get(id);
        // 缓存为空,过期了
        if (desc == null) {
    
    
            // 互斥锁,只有一个请求可以成功
            if (redis(lockName)) {
    
    
                try
                    // 从数据库取出数据
                    desc = getFromDB(id);
                    // 写到 Redis
                    redis.set(id, desc, 60 * 60 * 24);
                } catch (Exception ex) {
    
    
                    LogHelper.error(ex);
                } finally {
    
    
                    // 确保最后删除,释放锁
                    redis.del(lockName);
                    return desc;
                }
            } else {
    
    
                // 否则睡眠200ms,接着获取锁
                Thread.sleep(200);
                return getData(id);
            }
        }
}

缓存穿透

  当用户访问的数据,既不在缓存中,也不在数据库中,导致请求在访问缓存时,发现缓存缺失,再去访问数据库时,发现数据库中也没有要访问的数据,没办法构建缓存数据,来服务后续的请求。那么当有大量这样的请求到来时,数据库的压力骤增,这就是缓存穿透的问题。

  应对缓存穿透的解决方案有以下几种

  • 非法请求的限制:防止恶意请求,故意请求不存在的数据
  • 设置空值或者默认值:当请求的数据不存在 Redis 也不存在数据库的时候,设置一个缺省值(比如:None)。当后续再次进行查询则直接返回空值或者缺省值
  • 使用布隆过滤器:在写入数据库数据时,使用布隆过滤器做个标记,然后在用户请求到来时,业务线程确认缓存失效后,可以通过查询布隆过滤器快速判断数据是否存在,如果不存在,就不用通过查询数据库来判断数据是否存在,即使发生了缓存穿透,大量请求只会查询 Redis 和布隆过滤器,而不会查询数据库,保证了数据库能正常运行,Redis 自身也是支持布隆过滤器的。

猜你喜欢

转载自blog.csdn.net/weixin_43839871/article/details/130756925