缓存的穿透,击穿,雪崩
theme: csdn
highlight: redis
一. 缓存穿透
产生原因:用户在使用不存在的值去恶意访问,而缓存中不存在就会产生该请求不断db,造成数据库访问压力过大。
解决方案:
1. 如果是用户访问就去做鉴权校验,并对规定参数做限制大于或者小于亦或者符合自己所定义的规格。
2. 使用布隆过滤器排除不可能做缓存的请求:
实现原理:把所有可能存在问题的数据hash到一个足够大的bitmap中,如果一个一定不存在的数据会被这个bigmap直接拦截掉,从而避免了底层存储系统的查询压力。
优点:
1. 省空间:位是很省空间的,很长的东西也可以变为几位。
2. 省时间:判断,操作都是很快的,hash算法转化为向量也很快。
缺点:使用布隆会出现精准度损失的问题,而Bloom给出的默认损失精度的概率为3%,但是这个精准度的损失是可控的。
<!--guava-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
public class BloomFilterTest {
public static void main(String[] args) {
BloomFilter bloomFilter= BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()),1000000,0.001);
bloomFilter.put("name");
System.out.println(bloomFilter.mightContain("name"));
}
}
二. 缓存击破
产生原因:缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
解决方案:
1. 设置热点缓存永不过期。
2. 加互斥锁,从而解决缓存穿透。
@Service
public class RedisLockTest implements RedisLockTestInt{
private Lock lock = new ReentrantLock();
@Autowired
private RedisTemplateUtils redisTemplateUtils;
public String redisLockTest(String key) {
Object o = redisTemplateUtils.get(key);
String result = "";
if (o == null) {
if (lock.tryLock()) {
try {
System.out.println("访问数据库... 查到了" + "result");
result = "result";
redisTemplateUtils.set(key, result);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} else {
try {
Thread.sleep(200);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
redisLockTest(key);
}
}
return result;
}
}
三. 缓存雪崩
产生原因:缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。
解决方案:
1. 分布式锁:若只有一个线程能够获得锁,先判断缓存是否存在,不存在便从db中获取数据并更新缓存,存在缓存便阻塞其它进程,更新完所有缓存完成后再获取缓存并释放。
2. 数据预热:提前将所有热点数据缓存好,不用等用户请求再缓存数据,如果有大量数据便交由后台运维,如果数据并不是很大,便自动加载。
3. 缓存降级:当缓存A失效时,便请求备份B,A的失效时间短,B的失效时间设置长。
4. 错峰更新缓存:更新缓存时与用户请求高峰错开,规定其更新缓存过期时间策略。
5. 设置不同的过期时间:让缓存时间均匀,这样会使很多用户的缓存数据保存下来。
6. 设置热点数据永不过期。
7. 如果缓存是分布式布置,将热点数据分布均匀的分布在不同的缓存数据库中。