这是缓存系列的第三部分(终章)。

在实现缓存系统时,选择最佳缓存策略固然重要,但同样关键的是解决可能出现的运维挑战。本节将按类别深入探讨常见的缓存问题,并提供有效应对这些挑战的见解。

缓存可靠性问题

缓存可靠性对于维持系统稳定性至关重要。常见的可靠性问题包括缓存雪崩(cache avalanche)、缓存击穿(cache stampede)和缓存穿透(cache penetration)。

缓存系统在保护数据库方面发挥着至关重要的作用,它通过维持高缓存命中率来减少流向存储系统的流量。在正常情况下,大多数读取请求都会命中缓存,只有一小部分会导致缓存未命中。按照设计,缓存处理大部分流量,存储系统承受的负载要小得多。

缓存雪崩

缓存雪崩和缓存击穿是相关但不同的现象,它们可能发生在大规模系统中,导致严重的性能下降甚至系统级中断。缓存雪崩发生在多个或所有缓存条目同时或在短时间内过期时,导致对底层数据存储的请求突然激增。

缓存雪崩示意图

缓存击穿

缓存击穿(也称为 thundering herd)发生在大规模系统中,当大量请求突然涌入使系统不堪重负,导致性能下降甚至系统级中断。这可能由多种原因引起,例如热门项目的缓存未命中、用户流量突然激增,或维护后服务重启。

缓存击穿/惊群效应

当缓存或缓存的一部分发生故障时,大量读取请求导致缓存未命中,造成大量流量涌入存储系统。这种流量的突然增加会显著降低存储性能,甚至导致系统崩溃。在这种情况下,缓存故障成为一个触发器,结合了缓存雪崩和缓存击穿的元素,导致存储系统承受高压。

流量峰值(例如美国电商网站黑色星期五促销期间的高流量事件)也可能触发这些现象。单个缓存节点可能因对热门键的需求增加而崩溃,导致这些键被重新哈希到另一个节点,然后该节点也崩溃,最终导致整个缓存系统崩溃。

为了维持大规模系统的稳定性和效率,必须实施策略来降低缓存雪崩和缓存击穿的风险。有几种技术可以帮助预防和减轻这些事件的影响。

对于缓存雪崩预防,使用交错过期时间,即将基础 TTL 值与随机增量结合。这种方法可以分散缓存条目的过期时间,减少大量项目同时过期的可能性。

交错过期时间

一致性哈希可用于将缓存条目均匀分布在多个缓存服务器上。这种技术通过在剩余服务器之间分担负载并防止单点故障,减少缓存雪崩和缓存击穿的影响。

在系统中实施熔断器有助于防止缓存雪崩和缓存击穿升级为更严重的问题。熔断器监控系统健康状态,如果系统处于高压之下,通过停止对缓存和数据存储的过度请求来防止过载。

采用请求速率限制和负载剥离可以专门解决缓存击穿问题,控制请求处理速率,防止系统在负载过高时不堪重负。这些技术可以按每用户、每客户端或系统范围应用,以在高负载情况下维持稳定性。

缓存穿透

当应用程序尝试访问不存在的数据时,会发生缓存穿透,绕过缓存并可能导致性能问题。当应用程序请求不存在的键时,会导致缓存未命中,然后向存储系统发送请求,存储系统也返回空结果。这种绕过缓存到达后端存储的行为被称为缓存穿透。如果发生大量对不存在键的请求,可能会影响存储层的性能并使整个系统不稳定。

缓存穿透示意图

缓存穿透的一个常见例子是,众多用户在短时间内尝试查询网站上不存在的订单。大型网站通常会实施保护措施来缓解这个问题。

为了缓解这个问题,可以在缓存中存储一个占位符值来表示不存在的数据。后续对相同不存在数据的请求将命中缓存而不是数据存储。为这些占位符条目设置适当的 TTL,以防止它们无限期占用缓存空间。

使用不存在键的缓存占位符值

虽然这种方法简单有效,但如果应用程序频繁查询大量不存在的键,它可能会消耗大量缓存资源。

另一种解决方案涉及使用 布隆过滤器,这是一种节省空间的数据结构,可以概率性地测试元素是否属于某个集合。通过使用布隆过滤器,系统可以快速识别不存在的数据,减少缓存穿透和不必要的数据存储请求。

当记录添加到存储时,其键也会记录在布隆过滤器中。当获取记录时,应用程序首先检查键是否存在于布隆过滤器中。如果键不存在,它也不会存在于缓存或存储中,应用程序可以直接返回空值。如果键存在于布隆过滤器中,应用程序继续像往常一样从缓存和存储读取键。由于布隆过滤器有时会产生假阳性,少量缓存读取会导致缓存未命中。

缓存前的布隆过滤器

使用布隆过滤器的挑战在于其容量限制。例如,10 亿个键、1% 假阳性率需要约 1.2 GB 容量。此解决方案最适合较小的数据集。

热点 Key 问题

当单个键的高流量导致缓存节点承受过大流量压力时,就会发生热点 key问题,可能导致性能问题。

在分布式缓存系统中,键根据分片策略在不同节点之间共享。每个键的流量可能有很大差异,有些键的流量异常高。如下图所示,“key12”和”key15”等键的大量流量可能会超过节点 1 的资源容量。

例如,当推文按 ID 分布在缓存系统中时,其中少数推文迅速走红,相应的缓存节点流量增加,可能超过其资源能力。热点 key 问题可能出现在各种场景中,例如紧急运营事件、节日、体育比赛、闪购等。

分布式缓存中的热点 key

为了解决这个问题,需要采取两个步骤:

  1. 识别热点 key
  2. 对这些 key 实施特殊措施以减少流量压力

在步骤 1 中,对于可预测的事件(如重要节日或在线促销),可以预先评估潜在的热点 key。然而,对于突发事件,预先评估不可行。一种解决方案是进行实时流量分析,及时检测新出现的热点 key。

在步骤 2 中,有几种潜在的解决方案:

  • 将整个缓存系统的流量压力分散开。如下图所示,将”key12”拆分为”key12-1”、“key12-2”,直到”key12-N”,并将这 N 个键分布在多个节点上。当应用程序请求热点 key 时,它随机选择一个带后缀的键,使流量能够分散到许多节点上,防止单个节点过载。

热点 key 分散策略

  • 如果存在大量热点 key,实时监控可以使缓存系统快速扩展,减少流量影响。

  • 作为最后手段,应用程序可以将热点 key 存储在本地缓存中,减少对远程缓存系统的流量。这种方法减轻了缓存节点的压力,有助于维持整体系统性能。

大 Key 问题

当键的值大小非常大时,可能导致访问超时,从而产生大 key问题。

大 key 问题的影响比最初看起来更严重:

  • 频繁访问大 key 会消耗大量网络带宽,导致查找性能缓慢
  • 对大 key 的任何部分更新都会导致整个值的修改,进一步加剧性能问题
  • 如果大 key 失效或从缓存中驱逐,从存储重新加载它可能非常耗时,导致额外的慢查询性能

大 key 问题

本文为学习目的的个人翻译,译文仅供参考。

原文链接:A Crash Course in Caching - Final Part

版权归原作者或原刊登方所有。本文为非官方译本;如有不妥,请联系删除。