大致讲一下 缓存雪崩, 缓存穿透, 缓存击穿 的场景以及常规解决手段

缓存雪崩

场景:

在服务高峰期时出现大面积缓存过期的情况, 导致数据库请求被打满甚至宕机/被动构建缓存时cpu占用率暴升

解决方式
  • 缓存过期时间+/- 一个随机值, 尽量让缓存过期交错进行
  • 缓存集群部署, 缓存分布在各个节点中也能产生类似的效果
  • 不设置缓存过期时间, 而是数据更新后通过排它锁更新缓存

缓存穿透

场景:

比如按缓存了id=[1,2,3]的数据, 有一个ip不断请求id=-1 或 id=[4,5,6]这些未缓存到的数据, 导致缓存形同虚设, 增加数据库负载

或者id=1的缓存数据失效后读库时发生了错误, 也会导致所有请求id=1的请求都会直接链接数据库

解决方式:
  • 接口校验请求参数, 规避一些明显的错误格式
  • 设置数据边界, 比如mysql分页时最大page=100, 之后就不能用offset去做偏移,而是根据日期或者id去直接读取
  • 针对不存在缓存中的, id=-1的, 构建缓存时报错的也给缓存一个时间短一点的值, 就直接是null就好
  • 布隆过滤器

举例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
function demo($catchKey, $callback){
    try{
        $value = $this->getValueFromCache($catchKey);
        if($value === false){
            $this->setCache($catchKey, [], 30);
            return [];
        }
        if(!empty($value)){
            return $value;
        }

        $value = $callback($catchKey);
        $this->setCache($catchKey, $value, 3600)
        return $value;
    }catch(\Exception $e){
        $this->logException($e);
        $this->setCache($catchKey, [], 30);
        return [];
    }

}

缓存击穿

场景:

和缓存雪崩类似, 这里是单个热数据缓存失效, 导致流量像锥子一样穿透层层缓存的保护直达数据库, 然后数据库跪了…

解决方式:
  • 热点数据不设置缓存过期时间, 主动构建缓存, 加互斥锁
  • 布隆过滤器
  • 碰到这种情况时说明有了一定的流量, 那么就要考虑熔断的机制, 限制服务接收请求频次, 牺牲部分用户体验, 但是保证整体服务可用