欢迎访问我的技术博客

缓存雪崩、缓存穿透、缓存击穿等问题的应对

缓存雪崩、缓存穿透、缓存击穿等问题的应对

缓存雪崩

情况:大量缓存设置了相同的过期时间,导致某一刻同时失效,全部请求都到达db,造成db压力过大崩溃。或者缓存服务器出了故障,许多数据查不到缓存,直接请求到了db。

应对:

  • 可以在key的失效时间上加点随机值,避免集体失效。
  • 也可以设置缓存永久有效。然后通过后台线程来更新,而不是业务线程更新。
  • 缓存失效后,用加锁或队列的方式保证缓存的单线程写,从而只有一个线程落入db,后面的都能继续从缓存中获取。
  • 对于缓存服务器故障进行预防–高可用架构(用Redis Sentinel 和 Redis Cluster 实现高可用)
  • 如果缓存出了故障,要设计限流、熔断、降级方案,保证核心服务还能用。(可以配置开关人工降级,或者根据一些关键数据设计自动降级。 降级策略、规则(哪些边缘服务不提供,那些必须提供等)、发送报警这些需要实现考虑配套)
  • 准备好redis数据备份和恢复(做好持久化)、缓存预热这些方案,事发后快速恢复缓存数据。
  • 可以提前演练,尝试缓存出问题系统如何应对,如何快速解决。

 

缓存穿透

情况:查询一个不存在的数据,缓存中查不到,就会去存储层查询。在高并发情况下,如果有人故意查询不存在的key,则大量请求穿透了缓存层,形成一种对db直接的攻击。

应对:

  • 对key设置规则,过滤掉不符合规则的key
  • 把空结果作为一个值存入缓存,保持一个较短的有效时间。
  • 或者使用布隆过滤器(BloomFilter),将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

 

缓存击穿

情况:不同于缓存穿透(没命中),这里指缓存成为“热点”,有大量高并发请求击中它,而如果这个缓存失效瞬间仍很多请求,会造成大量请求压到db,可能压垮db。

应对:

  • 使用redis互斥锁,保障只有一个请求去读db,然后存入缓存,其他的请求不会到db。(使用set lock-xxx true ex 5 nx,一条原子指令,相当于setnx lock-xxx true 与 expire lock-xxx 5合并)

请求代码实例大致如下:(代码并不完美,比如$redis和$db直接这样出现会报错,只是写出来表示一下思路)

public get($key) {
    $value = $redis->get($key);   //先从redis查询
    if ($value == null) {         
        if ($redis->setnx($key_mutex, 1, 60) == 1) {             
            $value = $db->get($key);//从db中查询数据                   
            $redis->set($key, $value, $expire_secs=300);                      
            $redis->del($key_mutex);            
        } else {
              sleep(1);                      
              get($key);
          }
    } else {              
        return $value;             
    } 
}

热点数据问题

情况:遇到到哪些数据可能是热点,考虑应对热点数据查询。

应对:提前预热,把数据存入缓存中,避免请求时查询db。对于特别热门的,某一条请求量极端大,可以复制多份,把请求分散到多台缓存服务器上。

 

 

 

 

gaomingyang

评论已关闭。