YNZH's Blog

缓存穿透,缓存击穿,缓存雪奔

缓存穿透

什么是缓存穿透?

存在于查找的数据在缓存和数据库中都不存在时

当客户端请求查询一条既不在reids也不在数据库中的数据时,就会一直查询数据库,当并发量很大时就会造成数据库挂掉,有以下两种解决方案:

  1. 缓存空对象
  2. 布隆过滤器

缓存空对象

每次访问数据库不存在的时候就返回null,同时在reids中缓存空对象,这样下次就可以直接在缓存中命中,但是这样会存在一个问题就是redis中会有很多空对象占用内存空间,可以对空对象设置一个较短的过期时间.

布隆过滤器

布隆过滤器可以想象成一个集合,每次来一条数据的时候可以很快判断是否在这个集合中.但是有一点小小的缺陷就是会产生一个很低误报率,也就是说本来一个不在集合中的元素会错误的返回在集合中. 那么我们就可以使用布隆过滤器来判断请求查询的元素在不在数据库中,若布隆过滤器返回不存在,则说明肯定不存在直接返回空即可.若在,则又很低的几率其实不在但被误报了,但影响也不大.布隆过滤器有一个缺点就是需要维护.插入数据库中要进行更新,而且如果删除数据库中的某行元素的话维护比较麻烦.所以适用于库存很稳定的场景下使用.

缓存击穿

什么是缓存击穿?

存在于查找的数据在数据库中, 但是不在缓存中(可能刚好在redis中过期)

情况1:当很大的并发访问reids中某个热点数据的时候,这个key突然失效.导致大量的并发直接击穿缓存打到数据库中

情况2:当很大的并发访问某些冷门数据,还未缓存到redis中,也会导致情况1发生.

解决方案:加锁或者设置热点数据永不过期(对于情况1)

一个很简单的解决方案就是根据redis中key返回为空的时候,接下来防止大量并发打到数据库中对数据库请求加锁,所有并发排队,当第一个请求从数据库中拿到数据之后存入缓存中,释放锁.其他并发访问直接从缓存中拿数据就行了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class CachedData {
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) {
update(data);
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock(); // Unlock write, still hold read
}
}

try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}

缓存雪崩

在缓存中同时存在大量数据过期或者redis突然宕机,,而此时有大并发访问打到缓存中导致数据库难以承受,和缓存击穿不同的是缓存击穿是从一个点击穿,也就是一条数据过期,而雪崩是大批量的数据过期从而造成数据库的压力.

解决方案

1. 设置不同的过期时间

2. 设置永不过期

3. 将热点数据设置均匀分布在不同的缓存数据库中,搭建高可用的redis集群


 评论


博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

本站使用 Material X 作为主题 , 总访问量为 次 。
载入天数...载入时分秒...