Redis:缓存穿透、缓存雪崩、缓存击穿

只要说到Redis,当然离不开这经典三板斧:缓存穿透、缓存雪崩、缓存击穿。

一、缓存穿透

          故意去请求缓存中不存在的数据,从而去数据库中查,数据库中也没有,所以无法加到缓存,下次还是直接怼到数据库,所以高并发的时候就导致数据库崩了。

解决方案:

1)利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试。

2)数据库没查到数据,也往缓存中写入一个空值,但是设置失效时间短一点,防止恶意攻击。

二、缓存雪崩

          即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。

解决方案:

1)给缓存失效时间,加上一个随机值,避免大量缓存集体失效。

2)双缓存:缓存A和B,比如A的失效时间是20分钟,B不失效。比如从A中没读到,就去B中读,然后异步起一个线程同步到A。

3)也可以使用互斥锁,但是会影响性能,因为这个是业务场景,不是恶意攻击。

三、缓存击穿

          是指一个key非常热点(类似于爆款),在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

解决方案:

1)使用互斥锁(mutex key)

这里主要是要了解Redis Setnx 命令,简单来说利用此命令进行加锁,然后让获得锁的线程进行设置缓存,没有获得锁的进行等待,待前面的那个设置完成后,其他的能从缓存中得到数据。

//2.6.1前单机版本锁
String get(String key) {  
   String value = redis.get(key);  
   if (value  == null) {  
    if (redis.setnx(key_mutex, "1")) {  
        // 3 min timeout to avoid mutex holder crash  
        redis.expire(key_mutex, 3 * 60)  
        value = db.get(key);  
        redis.set(key, value);  
        redis.delete(key_mutex);  
    } else {  
        //其他线程休息50毫秒后重试  
        Thread.sleep(50);  
        get(key);  
    }  
  }  
}

最新版本

public String get(key) {
      String value = redis.get(key);
      if (value == null) { //代表缓存值过期
          //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db
		  if (redis.setnx(key_mutex, 1, 3 * 60) == 1) {  //代表设置成功
               value = db.get(key);
                      redis.set(key, value, expire_secs);
                      redis.del(key_mutex);
              } else {  //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
                      sleep(50);
                      get(key);  //重试
              }
          } else {
              return value;      
          }
 }

 

2)不设置过期时间,缓存数据为永久态。

 

你可能感兴趣的:(Redis)