缓存技术常见的问题

缓存雪崩

认识

当大量的缓存数据在同一时间失效,导致大量的请求直接打到数据库上,这种现象称为缓存雪崩。

解决方案

比较常见的做法是在存入缓存时,为它们设置不同的失效时间,避免大量缓存在同一时间失效。

缓存穿透

认识

缓存穿透是指查询一个缓存中不存在的数据,同时数据库中也不存在该条数据。

举个栗子:

假设系统被恶意攻击,查询一个不存在的用户,如用户 ID 为 -1, 按照之前的逻辑,会先从 Redis 中查询,缓存中不存在,然后会走数据库查询,依然不存在,那么此类请求永远都会打到数据库。如果被恶意发起大量此类请求,缓存形同虚设,依然会带来数据库雪崩,直接导致整个服务不可用。

解决方案

有两种常见策略:

  • 当数据库中找不到某条数据时,在 Redis 中缓存一个空值,表示该条数据不存在。这样后续的此类请求都会命中缓存,挡住非法请求对数据库的压力;

    这样也会带来另一个问题,如果被恶意请求大量不同的非法数据,如用户 ID 为 -1,-2,-3 …,此方案会使 Redis 中存储大量无用的空数据,极端情况下,甚至会驱逐出合法的缓存数据,导致缓存命中率大大降低,数据库再次面临访问量激增的风险。

    为了解决此问题,可以给空值缓存设置一个较短的过期时间,防止 Redis 内存占用持续增加。

  • 引入布隆过滤器;

    布隆过滤器(Bloom Filter)是一种数据结构,用于判断一个元素是否在一个集合中。它的特点是空间利用率高、查询速度快,但有一定的误判率:对“数据不存在”的判定是准确的,对“数据存在”的判定可能误判。

    布隆过滤器适合用来防止缓存穿透,它的处理流程大致如下:

    • 将数据库中的所有用户数据加入布隆过滤器中;
    • 当查询某条用户数据,且 Redis 中不存在时,开始检查布隆过滤器是否记录了此数据;
    • 若布隆过滤器认为“不存在”,由于这类判断是准确的,可以认为数据库中也不存在该数据,直接响应该用户不存在;
    • 若布隆过滤器认为“存在”,则走数据库查询,并加入 Redis 缓存中。虽然有一定误判,但误判率较低,只会缓存少量空值,不会因大量无用空数据把合法数据挤出缓存、拉低命中率。

缓存击穿

认识

缓存击穿是指某个热点数据在失效的瞬间,大量的并发请求直接打到了数据库上,导致数据库压力过大。

解决方案

可选的方案:

  • 缓存不设置过期时间:缓存不会过期被删除,自然不会有缓存击穿的风险;

  • 加分布式锁;

    当一个热点数据在缓存中失效时,可以使用分布式锁来确保只有一个服务实例去数据库中获取数据并更新缓存。这样可以避免大量请求同时访问数据库。

  • 引入本地缓存,和 Redis 形成二级缓存,互成犄角之势,两种缓存的过期时间设置得不一样。