解析redis缓存雪崩、缓存击穿和缓存穿透

1. 什么是缓存雪崩?

缓存雪崩是指在一个时间段内,缓存服务器上的大量缓存数据同时过期或失效,导致大量请求直接打到数据库上,使得数据库瞬间压力过大,甚至宕机,从而影响整个系统的稳定性与可用性。

1.1 缓存失效带来的危机

缓存的作用在于加速网站或应用的访问速度,缓解数据库的压力,提升整体性能。但缓存本身存在有效期,当缓存数据过期之后,又需要从数据库中重新读取数据,如果大量的缓存同时失效,那么系统会承受着一波又一波的请求,直到缓存数据全部刷新,数据库得到缓解。

1.2 缓存雪崩的案例

下面我们来看一个缓存雪崩的案例:

```

String get(String key) {

String value = redis.get(key);

if (value == null) {

String dbValue = db.get(key);

redis.set(key, dbValue, 1, TimeUnit.MINUTES);

return dbValue;

}

return value;

}

```

在以上代码中,当缓存中获取不到对应的数据时,会从数据库中读取数据,并且将其放入缓存中并设置有效期为1分钟。如果此时有大量的数据过期,那么大量的请求都会打到数据库中,导致数据库瞬时达到峰值并宕机。

1.3 如何解决?

在解决缓存雪崩问题时,需要对于缓存的失效时间进行随机化,加上过期时间的随机性,保证缓存不会在同一时间大面积失效。另外,我们还可以在缓存层增加限流措施或者使用互斥锁进行控制,以减少对数据库的冲击。

2. 什么是缓存击穿?

缓存击穿是指缓存中不存在但数据库中存在的数据,当请求过来,此时缓存中没有相应的值,就会直接查询数据库,如果请求的数据没有被加入到缓存中,则会让大量的请求直接打到数据库上,导致数据库瞬时达到峰值并宕机的情况。

2.1 缓存击穿的案例

下面我们来看一个缓存击穿的案例:

```

String get(String key) {

String value = redis.get(key);

if (value == null) {

String dbValue = db.get(key);

redis.set(key, dbValue, 1, TimeUnit.MINUTES);

return dbValue;

}

return value;

}

```

在以上代码中,如果请求的数据在缓存中不存在且在数据库中存在,那么仍然会直接从数据库中读取数据。这种情况下,如果请求特别集中,就会导致请求大量打到数据库上。

2.2 如何解决?

在解决缓存击穿问题时,需要使用互斥锁对于查询结果进行加锁,当多个请求同时访问同一个缓存数据时,只有一个进行查询操作,其他请求进行等待,当查询完成后,其他请求再次读取缓存即可。另外,我们还可以在缓存层增加限流措施,防止瞬间请求量过大。

3. 什么是缓存穿透?

缓存穿透是指查询一个一定不存在的数据,由于缓存中没有相应的值,每次请求都会直接打到数据库上,对于缓存和数据库都造成很大的压力,甚至会因为非法访问导致系统宕机。

3.1 缓存穿透的案例

下面我们来看一个缓存穿透的案例:

```

String get(String key) {

String value = redis.get(key);

if (value == null) {

String dbValue = db.get(key);

if (dbValue == null) {

redis.set(key, "", 1, TimeUnit.MINUTES);

return null;

}

redis.set(key, dbValue, 1, TimeUnit.MINUTES);

return dbValue;

}

return value;

}

```

在以上代码中,当缓存中获取不到对应的数据时,会从数据库中读取数据,并且将其放入缓存中并设置有效期为1分钟。但是如果有人恶意攻击,每次请求都是一份不存在的数据,那么就会导致瞬时打到数据库上。

3.2 如何解决?

在解决缓存穿透问题时,需要对于非法请求进行过滤,对于查询结果为空的缓存值,也需要写入缓存,防止下次再有非法请求时直接打到数据库上,进而加重数据库的负担。另外,我们还可以使用布隆过滤器等技术对于可能出现的非法请求进行拦截。

数据库标签