Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于Redis缓存穿透、缓存雪崩、缓存击穿问题探究 #13

Open
superleeyom opened this issue Dec 23, 2020 · 0 comments
Open

关于Redis缓存穿透、缓存雪崩、缓存击穿问题探究 #13

superleeyom opened this issue Dec 23, 2020 · 0 comments
Assignees

Comments

@superleeyom
Copy link
Owner

@superleeyom superleeyom commented Dec 23, 2020

缓存穿透

拿一个不存在的 key 去查询数据,如果缓存里面查询不到,就会去数据库里面查询,如果有人恶意拿不存在的 key 疯狂请求,会把数据库压垮,这就是缓存穿透,下面用一段伪代码:

List<String> cacheList = redis.get(key);
if(CollUtil.isEmpty(cacheList)){
	List<String> list = mysql.getList(key);
	if(CollUtil.isNotEmpty(list)){
		redis.set(key,list,3 * 60);
	}
  return list;
}
return cacheList;

通常来说,解决缓存穿透有两种方式:

  • 为不存在的 key 设置空值

    • 伪代码如下:

      List<String> cacheList = redis.get(key);
      if(CollUtil.isEmpty(cacheList)){
      	// 不管有没有在数据库中查询到数据,都给key设置值   
      	List<String> list = mysql.getList(key);
      	redis.set(key,list,3 * 60);
        return list;
      }
      return cacheList;
  • 使用布隆过滤器

缓存雪崩

在某个时间点,大批的 key 出现过期,导致所有的请求全部打到数据库上,把数据库压垮,这种就是缓存雪崩,通常解决缓存雪崩有如下的几种方案:

  • 永不过期:设置 key 永不过期,但是这种会占用服务器挺多内存;
  • 过期时间错开:比如这个 key 设置的过期时间是 5 分钟,那另外一个 key 设置的过期时间则为 7 分钟,把过期时间错开,防止在某个时间点同时失效
  • 多缓存结合:在数据库和 Redis 再加一层缓存,比如 Memcache,这样的话,缓存一旦过期,Memcache 里面还可以顶一会儿;
  • 采购第三方的 Redis 服务:现在很多云平台都有推出 Redis 服务,有单机的,集群的,可以根据自己的使用场景去采购,当然人家也帮你处理好了这些问题,有钱啥问题都能解决。

缓存击穿

当前的某个热点 key 缓存过期,同一时间,有大量的请求同时来访问这个 key,导致所有的请求都打到数据库上去了,把数据库压垮。那通常遇到这种问题的话,一般就是使用排斥锁,当然也有一种粗暴的办法,就是设置永不过期,但是这种粗暴方式,大多数情况下不适用。

关于排斥锁,可以这样理解,第一个请求达到请求 key 发现缓存里面没有,允许它去数据库查询,同时加锁,这样第二个请求,第三个请求…都会被锁阻塞到当前,当第一个请求从数据库查询到数据后,将数据缓存到 Redis 中,然后释放锁,这样第二个,第三个请求...,就直接可以从缓存中拿数据,就不会再打到数据库,这样就减少了数据库的并发压力。

String get(String key) {  
   String value = redis.get(key);  
   if (value  == null) {  
    if (redis.setnx(key_mutex, "1")) {  
        // 给锁设置一个过期时间,防止持有锁的人挂了,导致锁不能释放
        redis.expire(key_mutex, 3 * 60)  
        // 从DB中查询数据并缓存
        value = db.get(key);  
        redis.set(key, value);
      	// 释放锁
        redis.delete(key_mutex);
      	return value;
    } else {  
        //其他线程休息100毫秒后重试  
        Thread.sleep(100);  
        get(key);  
    }  
  }
  return value;
}

其实对于这些热点 key,最好还是有个独立的服务,去定时的刷新缓存,这样的话,很大的程度上可以避免这种问题。

@superleeyom superleeyom self-assigned this Dec 23, 2020
@superleeyom superleeyom changed the title 关于Redis缓存穿透、缓存雪崩、缓存击穿问题探讨 关于Redis缓存穿透、缓存雪崩、缓存击穿问题探究 Dec 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant