0
点赞
收藏
分享

微信扫一扫

Redis缓存穿透、击穿、雪崩问题

一脸伟人痣 2022-03-27 阅读 92

1.一般的缓存处理流程

当前台收到请求后,后台先回冲缓存中读取数据,取到直接返回结果,当从缓存取不到结果时,就会访问数据库,从数据库取到数据更新到缓存中,并返回结果,如果数据库也没结果,就返回空。

2. 缓存穿透

描述:
访问一个缓存和数据库都不存在的 key,此时会直接打到数据库上,并且查不到数据,没法写缓存,所以下一次同样会打到数据库上。
如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。

缓存穿透流程图如下

解决方案

  1. 对空值缓存: 如果一个查询返回的数据为空(不管数据是否不存在),任然吧这个空结果(null)进行缓存,设置空结果的超过时间会很短,一般不超过五分钟。
  2. 设置可访问的名单(白名单): 使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量,每次访问和bitmap里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问。
  3. 采用布隆过滤器:(布隆过滤器(Bloom Filter))是1970年由布隆提出的。
    它实际上是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是由一定的误识别率和删除困难。
  4. **进行实时监控:**当发现Redis的命中率开始急速降低,需要排查访问对象和访问的数据,和运维人员配合,可以设置黑名单限制服务。

3.缓存击穿

描述:
某一个热点key,在缓存过期的一瞬间,同时有大量的请求打进来,由于此时缓存过期了,所以请求最终都会走到数据库,造成瞬时数据库请求量大、压力骤增,甚至可能打垮数据库。

解决方案:

  1. 预先设置热门数据: 在Redis高峰访问之前,把一些热门数据提前存入到redis中,加大这些热门数据key的时长。
  2. 实时调整: 现场监控哪些数据热门,实时调整key的过期时长。
  3. 使用锁: 在并发的多个请求中,只有第一个请求线程能拿到锁并执行数据库查询操作,其他的线程拿不到锁就阻塞等着,等到第一个线程将数据写入缓存后,直接走缓存。可以使用“分布式锁”,还是“JVM 锁”
    使用 redis 分布式锁的伪代码,仅供参考:
public Object getData(String key) throws InterruptedException {
    Object value = redis.get(key);
    // 缓存值过期
    if (value == null) {
        // lockRedis:专门用于加锁的redis;
        // "empty":加锁的值随便设置都可以
        if (lockRedis.set(key, "empty", "PX", lockExpire, "NX")) {
            try {
                // 查询数据库,并写到缓存,让其他线程可以直接走缓存
                value = getDataFromDb(key);
                redis.set(key, value, "PX", expire);
            } catch (Exception e) {
                // 异常处理
            } finally {
                // 释放锁
                lockRedis.delete(key);
            }
        } else {
            // sleep50ms后,进行重试
            Thread.sleep(50);
            return getData(key);
        }
    }
    return value;
}

4.缓存雪崩

描述: 大量的热点key设置了相同的过期时间,导致缓存在同一时刻全部失效,造成瞬时数据库大量请求、压力骤增,引起雪崩,甚至导致数据库被打挂。

解决方案:

  1. 过期时间打散: 既然是大量缓存集中失效,那最容易想到就是让他们不集中生效。可以给缓存的过期时间时加上一个随机值时间,使得每个key的过期时间分布开来,不会集中在同一时刻失效。
  2. 热点数据不过期: 该方式和缓存击穿一样,也是要着重考虑刷新时间的时间间隔和数据库异常如何处理的情况。
  3. 加互斥锁: 开方式和缓存击穿一样,按key维度加锁,对于同以可key,只允许一个线程去计算,其他线程原地阻塞等待第一个线程的计算结果,让后直接走缓存。
举报

相关推荐

0 条评论