缓存雪崩和缓存击穿的场景和解决方案

缓存穿透

什么是缓存穿透?

一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。如果key对应的value是一定不存在的,并且对该key并发请求量很大,就会对后端系统造成很大的压力。这就叫做缓存穿透。
 

如何避免?

1:对查询结果为空的情况也进行缓存,缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
2:对一定不存在的key进行过滤。可以把所有的可能存在的key放到一个大的Bitmap中,查询时通过该bitmap过滤。【感觉应该用的不多吧】

缓存雪崩

什么是缓存雪崩?

当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。

如何避免?

1:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
2:不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
3:做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期(此点为补充)

1.使用互斥锁(mutex key)

业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存;否则,就重试整个get缓存的方法。

SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利用它来实现锁的效果。在redis2.6.1之前版本未实现setnx的过期时间,所以这里给出两种版本代码参考:

[java]  view plain  copy
  1. //2.6.1前单机版本锁  
  2. String get(String key) {    
  3.    String value = redis.get(key);    
  4.    if (value  == null) {    
  5.     if (redis.setnx(key_mutex, "1")) {    
  6.         // 3 min timeout to avoid mutex holder crash    
  7.         redis.expire(key_mutex, 3 * 60)    
  8.         value = db.get(key);    
  9.         redis.set(key, value);    
  10.         redis.delete(key_mutex);    
  11.     } else {    
  12.         //其他线程休息50毫秒后重试    
  13.         Thread.sleep(50);    
  14.         get(key);    
  15.     }    
  16.   }    
  17. }  
最新版本代码:

[java]  view plain  copy
  1. public String get(key) {  
  2.       String value = redis.get(key);  
  3.       if (value == null) { //代表缓存值过期  
  4.           //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db  
  5.           if (redis.setnx(key_mutex, 13 * 60) == 1) {  //代表设置成功  
  6.                value = db.get(key);  
  7.                       redis.set(key, value, expire_secs);  
  8.                       redis.del(key_mutex);  
  9.               } else {  //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可  
  10.                       sleep(50);  
  11.                       get(key);  //重试  
  12.               }  
  13.           } else {  
  14.               return value;        
  15.           }  
  16.  }  
2. "永远不过期":  

这里的“永远不过期”包含两层意思:

(1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。

(2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期

        从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。

[java]  view plain  copy
  1. String get(final String key) {    
  2.         V v = redis.get(key);    
  3.         String value = v.getValue();    
  4.         long timeout = v.getTimeout();    
  5.         if (v.timeout <= System.currentTimeMillis()) {    
  6.             // 异步更新后台异常执行    
  7.             threadPool.execute(new Runnable() {    
  8.                 public void run() {    
  9.                     String keyMutex = "mutex:" + key;    
  10.                     if (redis.setnx(keyMutex, "1")) {    
  11.                         // 3 min timeout to avoid mutex holder crash    
  12.                         redis.expire(keyMutex, 3 * 60);    
  13.                         String dbValue = db.get(key);    
  14.                         redis.set(key, dbValue);    
  15.                         redis.delete(keyMutex);    
  16.                     }    
  17.                 }    
  18.             });    
  19.         }    
  20.         return value;    
  21. }  

 

分布式缓存系统

分布式缓存系统面临的问题

缓存一致性问题

1:缓存系统与底层数据的一致性。这点在底层系统是“可读可写”时,写得尤为重要 

2:有继承关系的缓存之间的一致性。为了尽量提高缓存命中率,缓存也是分层:全局缓存,二级缓存。他们是存在继承关系的。全局缓存可以有二级缓存来组成。 

3:多个缓存副本之间的一致性。为了保证系统的高可用性,缓存系统背后往往会接两套存储系统(如memcache,redis等)

缓存穿透和缓存雪崩
上面有讲述。
 

缓存数据的淘汰

缓存淘汰的策略有两种: (1) 定时去清理过期的缓存。 (2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。 
两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂,具体用哪种方案,大家可以根据自己的应用场景来权衡。
 
1. 预估失效时间 2. 版本号(必须单调递增,时间戳是最好的选择)3. 提供手动清理缓存的接口

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值