当前位置: 首页 > 工具软件 > redis-mutex > 使用案例 >

Redis--》缓存雪崩、击穿、穿透

马航
2023-12-01

本文有参考其他文章及部分个人理解,如有错误以及意见,欢迎交流。

缓存雪崩

理解:

key批量失效:大面积的缓存失效,请求打崩数据库同时大面积失效,redis等同于无,这个数量等级的请求直接打到数据库,如果没做熔断等策略,基本就瞬间挂,那么依赖这个库的所有接口都会报错。

解决方案:

  1. 往redis中存数据时,给没有设置过期时间的每个key加上随机失效时间,这样可以保证数据不会同一时间大面积失效。
  2. redis集群部署,将热点数据均匀分布在不同的redis库中也能避免全部失效的问题。
  3. 设置热点数据永不过期,有更新操作就更新缓存就好了。

缓存击穿

理解:

热点key失效瞬间:非常热点key,在不停的扛着大并发,并发集中在这个一个点,当key失效的瞬间,持续的大并发就击穿缓存。

解决方案:

  1. 设置热点数据永不过期 。
    缺陷:数据不能保持最新、定时线程更新数据。
  2. synchronized+双重检查机制:某个key只让一个线程查询,堵塞其他线程,在同步块中,继续判断检查,保证不存在,再去查数据库 。
    缺陷:会堵塞其他线程。

示例:

public String getValue(String key) {

	String value= redis.get(key, String.class);

	if (Tools.isEmpty(value)) {

		synchronized(lockHelp) {

			value = redis.get(key,String.class);

				if (Tools.isEmpty(value)) {

					value = db.query(key);

					redis.set(key, value, 1000);
				}
		}
	}
	return value;
}
  1. 增加互斥锁:在缓存失效的时候(根据判断从缓存中取出来的值是否为空),不是马上去数据库查询,而是先使用带成功操作返回值的方法去set一个mutex key(热点key),当操作返回成功时,再进行查询数据库的操作并回设缓存;否则,就重试整个get缓存方法。

示例

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);//查询db的值
				redis.set(key, value, expire_secs);//set进key
				redis.del(key_mutex);//删除热点key
				return value;
			} else {//这个时候代表同时间的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
			sleep(10);
			get(key); //重试
			}
		} else {
			return value;
		}
}

缓存穿透

理解:

不存在的key:用户不断发起缓存和数据库中均不存在的数据请求,导致数据库压力过大,严重会击垮数据库。

解决方案:

  1. 增加参数校验。
  2. 从网关层Nginx增加配置项,对单个ip美妙访问次数超出阈值的ip都拉黑。
  3. .布隆过滤器(Bloom Filter)能很好防止缓存穿透的发生,原理就是利用高效的数据结构和算法快速判断出这个key是否在数据库中存在,不存在就return,存在就去查数据库刷新缓存再return。
 类似资料: