一、缓存雪崩
1、产生原因:
(1)redis挂掉了,请求全部走数据库。
(2)对缓存数据设置相同的过期时间,导致某段时间内缓存失效,请求全部走数据库。
(3)缓存雪崩如果发生了,很可能就把我们的数据库搞垮,导致整个服务瘫痪!
2、解决方法:
(1)在缓存的时候给过期时间加上一个随机值,这样就会大幅度的减少缓存在同一时间过期。
(2)对于“Redis挂掉了,请求全部走数据库”这种情况,我们可以有以下的思路:
事发前:实现Redis的高可用(主从架构+Sentinel(哨兵) 或者Redis Cluster(集群)),尽量避免 Redis挂掉这种情况发生。
事发中:万一Redis真的挂了,我们可以设置本地缓存(ehcache)+限流(hystrix),尽量避免我们的 数据库被干掉(起码能保证我们的服务还是能正常工作的)
事发后:redis持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。
二、缓存穿透
1、产生原因:
(1)请求的数据在缓存大量不命中,导致请求走数据库。
(2)缓存穿透如果发生了,也可能把我们的数据库搞垮,导致整个服务瘫痪!
2、解决方法:
(1)由于请求的参数是不合法的(每次都请求不存在的参数),于是我们可以使用布隆过滤器或者压缩filter提前拦截,不合法就不让这个请求到数据库层!学习BloomFilter点这里 (2)当我们从数据库找不到的时候,我们也将这个空对象设置到缓存里边去。下次再请求的时候,就可以从缓存里边获取了。这种情况我们一般会将空对象设置一个较短的过期时间。
三、缓存击穿
1、产生原因:
缓存击穿,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库
2、解决办法:
(1)设置key永不过期,或者快过期时,通过另一个异步线程重新设置key;
(2)使用互斥锁的方式;
static Lock reenLock = new ReentrantLock();
public List<String> getData04() throws InterruptedException {
List<String> result = new ArrayList<String>();
// 从缓存读取数据
result = getDataFromCache();
if (result.isEmpty()) {
if (reenLock.tryLock()) {
try {
System.out.println("我拿到锁了,从DB获取数据库后写入缓存");
// 从数据库查询数据
result = getDataFromDB();
// 将查询到的数据写入缓存
setDataToCache(result);
} finally {
reenLock.unlock();// 释放锁
}
} else {
result = getDataFromCache();// 先查一下缓存
if (result.isEmpty()) {
System.out.println("我没拿到锁,缓存也没数据,先小憩一下");
Thread.sleep(100);// 小憩一会儿
return getData04();// 重试
}
}
}
return result;
}