缓存穿透、缓存击穿、缓存雪崩的区别
一、缓存穿透:(即:缓存无数据,数据库也无数据)
如黑客恶意攻击,使用缓存和数据库均没有的key进行不断请求,导致数据库压力过大。
解决方法:
1、对用户进行鉴权、对请求参数进行校验,不合理直接过滤。
2、对查询不到的数据也放到缓存,value为空,设置一定的过期时间。(不太常用,因为如果是随机key就不起作用,且占缓存)
3、使用布隆过滤器,快速判断key是否在数据库中存在,不存在直接返回。(最有效)
第1种是最常用的策略,第2种不太常用,因为如果是随机key就不起作用,且占缓存,第三种最简单有效。实际使用中,可以1、3相结合。
二、缓存击穿:(即:缓存无数据,数据库有数据,key比较集中)
如高并发的情况下,热点数据缓存过期,这时候会导致大量请求读不到缓存同时读数据库,导致数据库负载过大。
解决方法:
1、设置热点数据永远不过期。
2、热点数据快过期时,通过另一个异步线程重新设置key。
3、当从缓存数据过期,重新从数据库加载数据到缓存的过程上互斥锁。
第1种的话,数据量大时,缓存量会比较大,第2种,很好理解,但是需要另外的逻辑去维护,会增加系统的复杂度。第3种,是比较常用的方式。
加载缓存时上互斥锁:
1 public String get(String key) throws Exception { 2 String value = redis.get(key); 3 // 缓存过期 4 if (value == null) { 5 // 设置有效期,防止del操作失败时,缓存过期一直不能重新加载缓存 6 if (redis.setnx(key_mutex, 1, 60)) { 7 // 从数据库加载缓存 8 value = database.get(key); 9 redis.set(key, value, expire_time); 10 redis.del(key_mutex); 11 } else { 12 // 其他线程已经在加载缓存,等待并重新获取即可 13 sleep(50); 14 get(key); 15 } 16 } 17 return value; 18 }
三、缓存雪崩:(即:缓存无数据,数据库有数据,key比较分散)
如在高并发的情况下,缓存同一时刻失效(如缓存挂了,或者设置了相同过期时间),所有请求会读数据库,容易导致数据库负载瞬间上升,乃至崩掉。如果重启数据库,立马又会被新的请求压崩。
解决方法:
1、缓存的失效时间设置为随机值,避免同时失效。
2、redis搭建高可用,主从+哨兵,redis cluster。
3、服务限流、降级,避免数据库被瞬间压崩。
第1种只能防止因缓存同时过期导致的缓存失效,第2种可以有效避免单台缓存挂掉的情况。第3种是通过提高服务的高可用,来避免缓存失效带来的影响,是辅助措施。
缓存击穿和缓存雪崩区别:
两者有点像,主要区别在于缓存击穿是查询同一条数据或热点数据查询不到,穿过了缓存。缓存雪崩是指大量数据查询不到,穿过了缓存。
本文来自博客园,作者:PC君,转载请注明原文链接:https://www.cnblogs.com/pcheng/p/14780800.html