版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/li1987by/article/details/86703191
Redis击穿、穿透、雪崩解析
面对并发分布式缓存时,Redis可能遇到以下问题
一、击穿
1.问题场景
某个key的数据为热点数据,当缓存过期后,出现大量并发访问该key的情况。造成大量请求该key的访问,落到存储层,直接将存储层压垮。
2.解决办法
一般利用分布式锁–mutex。访问热点数据的线程,如果没有在缓存层获取到数据,只有一个可以获取到一把互斥锁,有资格访问存储层,然后将数据缓存到缓存层。其它需要睡眠等待后重新访问缓存层热点数据。
3.代码示例
public String get(String key){
//所有线程优先访问缓存层,获取数据
String value = redis.get(key);
//缓存层无该数据
if(value == null){
//设置互斥锁 --mutex
String mutex = key.concat(":mutex");
//获取带有过期时间的互斥锁,避免获取互斥锁的线程不能释放,造成后面线程阻塞
if (redis.setnx(keynx, VALUE, 3 * 60) == VALUE) {
//代表设置成功
value = db.get(key);
redis.set(key, value, expire_secs);
redis.del(keynx);
} else {
//这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可
sleep(50);
//重试
get(key);
}
} else {
return value;
}
}
或者
private static final String LOCKED_SUCCESS = "OK";
private static final String NX = "NX";
private static final String EXPIRE_TIME = "PX";
/*** 获取锁
*
* @param jedis redis客户端
* @param lockKey 锁的key
* @param uniqueId 请求标识
* @param expireTime 过期时间
* @return 是否获取锁
* *
* /
public static boolean tryDistributedLock(Jedis jedis, String lockKey, String uniqueId, long expireTime) {
String result = jedis.set(lockKey, uniqueId, NX, EXPIRE_TIME, expireTime);
return LOCKED_SUCCESS.equals(result);
}
二、穿透
1.问题场景
一般是恶意访问数据库中不存在的数据,必然不会存储在缓存中,从而可以直接访问数据层。大量这种请求攻击,就会使数据层挂掉
2.解决办法
1)缓存空值,但是最好设置一个较短的过期时间,如超过5分钟。
2)布隆过滤器
优点:简单,方便集成,空间效率、查询时间远超一般算法
缺点:容错性、不能很好支持大数据(费内存,不能横向扩展)
3.代码示例
1)引用
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
</dependencies>
初始化存储层数据映射到bitmap
private static final int capacity = 1000000;
private static final int key = 999998;
//第三个参数为出错率
private static BloomFilter bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8),capacity ,0.00000008);
@PostConstruct
public void init(){
List<String> list = orderMapper.query();
int listSize = list.size();
for(int i = 0; i < listSize; i++){
bloomFilter.put(list.get(i));
}
}
查询
public void queryBloom(){
int errorCount = 0;
String temp = "";
for(int i = 0; i < 10000; i++){
temp = "test" + i;
if(bloomFilter.mightContain(temp)){
errorCount++;
}
}
System.out.pringln("出错量:" + errorCount);
}
三、雪崩
1.问题场景
主要是大部分的key在同一时间过期,这是这些key有大量的并发访问,将直接访问存储层,使存储层宕掉。
2.解决办法
设置不同的key,不同的过期时间,如设置1~5分钟的随机数。