redis的进程锁避免缓存击穿

redis的进程锁避免缓存击穿

首先由于系统的特性知道哪些键是热键,针对这些热键,哪个线程发现自己现在拿的是空,马上锁住,获取向下操作的资格,其他阻塞(这样就不会对db压力大(不然都去访问db)),然后马上这UI个线程请求db获取数据填充缓存

缓存击穿

对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。

缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

解决方案

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 在CODE上查看代码片派生到我的代码片

//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);    

    }    

  }    

}  

最新版本代码:

[java] view plain copy 在CODE上查看代码片派生到我的代码片

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;        

          }  

猜你喜欢

转载自yuhuiblog6338999322098842.iteye.com/blog/2373409