1、缓存击穿出现的场景
我们知道redis的数据是存储在内存的,而内存是有限的,所以一般会设置过期时间,当某个key过期了,而此时大量的并发来请求这个key,导致都去请求Mysql啦,而mysql的并发连接数很低,缺少了redis这层盾牌,mysql自然扛不住,这不是架构的问题,因为之前的架构中有redis的,系统是扛得住的,雪崩是一种特殊情况。
2、缓存击穿的预防
代码加锁!!
传统获取缓存的写法:
if ($data = $Cache->get($cacheKey)) {
return $this->jsonSucess($data);
}else{
// 读取数据库
// 写入缓存
}
key已过期,现在有1w的并发同时到达,所以都去到mysql了。我们需要优化一下,使只有1个去到mysql,剩下的9999去到redis。
使用zookeeper分布式锁来控制。参考:https://blog.csdn.net/raoxiaoya/article/details/99651669
if ($data = $Cache->get($cacheKey)) {
return $this->jsonSucess($data);
}else{
$ZooLock->getLock();// 方法内部阻塞来获取锁,一定会拿到
// 读一次redis,如果有上一个请求,它一定会写入redis的,我就直接返回数据;如果我是第一个,那还是得去数据库
// Redis没有,读取数据库
// 写入缓存
// 释放锁
}
看起来获取锁的地方阻塞太严重了,可以优化为如果获取锁失败就读一次缓存,但是这样不利于封装,其实,大量并发走到else的情况也就在缓存失效的那一刻,在这一刻的性能会有所下降而已。
缓存雪崩:
大量的key集中过期,导致数据库压力瞬间增大。防止大量key在同一时间过期,将过期时间分散,每个key的过期时间上加一个随机数。