redis缓存击穿、穿透、热点key、雪崩方案总结

缓存击穿

首先,在将缓存击穿前,大家先来回忆下自己写缓存的方案,这里我简单画了下流程图:

redis缓存击穿、穿透、热点key、雪崩方案总结_第1张图片

当我们缓存key设置过期时间,恰巧在这一刻这个key在某一刻被高并发的访问,把所有的请求都打到了DB中这就可能会导致DB挂了。这个跟后面说的缓存雪崩非常相似,这个和缓存雪崩的区别在于这里针对某一key缓存,但是雪崩则指的是多个key,要解决方案有很多,比如让一个线程构建缓存,另外线程等待知道构建好,或者redis维护timeout字段逻辑失效等等

解决方案:

1、使用同步方式锁,单机用synchronized,lock可重入锁等,分布式则用redis的setnx。

String get(String key) {  

   String value = redis.get(key);  

   if (value  == null) {  

    String brokenKey = "broken_"+key;  

    if (redis.setnx(brokenKey, "1")) {  

        redis.expire(brokenKey, 60)  //防止产生key不失效

        value = db.get(key);  

        redis.set(key, value);  

        redis.del(brokenKey);   

    } else { 

        //其他线程休息50毫秒后重试   

        Thread.sleep(50);   

        get(key);   

    }   

  }  

return value;

}  

优点: 保证一致性

缺点:代码复杂度增大,存在死锁的风险

2、采用缓存物理不过期,逻辑过期方式,在key对应的value设置一个timeout字段。如果检测到存的时间超过过期时间则异步更新缓存。

String get(final String key) {  

        T t = redis.get(key);  

        String value = t.getValue();  

        long timeout = t.getTimeout();  

        // 当逻辑的超时时间到了时,异步构建缓存  

        if (timeout <= System.currentTimeMillis()) {  

            threadPool.execute(new Runnable() {  

                public void run() {  

                    String brokenKey = "broken_"+key;  

                    if (redis.setnx(brokenKey, "1")) {  //redis加锁

                        redis.expire(brokenKey, 60);  //防止产生key不失效

                        String dbValue = db.get(key);  

                        redis.set(key, dbValue);  

                        redis.del(brokenKey);  

                    }  

                }  

            });  

        }  

        return value;  

    }

优点: 不会阻塞线程 高性能

缺点:数据库和缓存可能会存在数据不一致

缓存穿透

看了上面介绍的缓存击穿了,现在怎么又出现了一个缓存穿透呢,感觉字面上穿透和击穿应该是一个意思啊,确实个人理解翻译上是差不多,但是对于redis的专业技术语上却是不同,缓存穿透指的是一些恶意人攻击,比如说登录的时候它请求一个一定不存在的用户名时,那么我们服务端正常应该是这样处理,首先到缓存中查询,没有在到数据查询,看看好像是没问题啊,查询一下应该很快,如果是这一刻恶意攻击发起几百万次请求,所以就会一直循环这个操作,一定不存在的用户名会一直查询数据库,那么你的数据库和缓存系统会不会挂呢?

解决方案

1、布隆过滤器处理,把key放到布隆过滤器中,获取时查看是否存在,如果存在则获取缓存、获取数据库(把key放入缓存又2种方案,1、业务系统初始化 2、缓存中获取不到时单条插入到布隆过滤器)

 

优点: 高性能

 

缺点:布隆过滤器不支持删除,需要单独维护一个缓存key的集合

 

2、查询数据库返回NULL时,不在是不放到redis中,而是redis中设置查询结果为空的标示,比如:redis.set("key_none","NONE") 

 

优点: 简单明了

 

缺点:需要为每个key再单独维护一个标示空的key

 

热点KEY

 

某个key访问非常频繁,当key在某个时刻失效的时候有大量线程来构建缓存,导致负载增加,系统崩溃。

解决方案

热点key也是缓存失效打到数据库,同缓存击穿一样的解决方案

 

缓存雪崩

缓存雪崩上面又提到过和缓存击穿类似,雪崩指的是多个key同时失效,当高并发的请求过来所有请求都打到数据库导致挂了

解决方案

1、尽量的把key的失效时间设置成不一样,这样可以减少并发带来的压力

2、同上缓存击穿方案

你可能感兴趣的:(数据库)