缓存高一致性:Meta的缓存失效解决方案


缓存有助于减少延迟、扩展读取繁重的工作负载并节省成本。它们实际上无处不在。缓存在您的手机和浏览器中运行。例如,CDN 和 DNS 本质上是地理复制缓存。多亏了许多在幕后工作的缓存,您现在可以阅读这篇博文。

Phil Karlton 有句名言:“计算机科学中只有两件困难的事情:缓存失效和命名事物。” 如果您曾经研究过使用失效的缓存,那么您很可能会遇到令人讨厌的缓存不一致问题。

在 Meta,我们运营着世界上一些最大的缓存部署,包括 TAOMemcache。多年来,我们将 TAO 的缓存一致性提高了一项,从 99.9999%(6 个 9)提高到 99.99999999%(10 个 9)。 

当谈到缓存失效时,我们相信我们现在有一个有效的解决方案来弥合理论与实践之间的差距。这篇博文中的原理和方法广泛适用于大多数(如果不是全部)任何规模的缓存服务。无论您是在 Redis 中缓存 Postgres 数据还是维护分解的具体化,它都会这样做。

我们希望帮助减少工程师必须处理的缓存失效问题的数量,并帮助使所有失效的缓存更加一致。

根据定义,缓存不保存数据的真实来源(例如,数据库)。缓存失效描述了当真实源中的数据发生变化时主动使陈旧的缓存条目失效的过程。如果缓存失效处理不当,它可能会无限期地在缓存中留下与事实来源不同的不一致值。

缓存失效涉及必须由缓存本身以外的其他事物执行的操作。有些东西(例如,客户端或发布/订阅系统)需要告诉缓存发生了突变。仅依赖生存时间 (TTL) 来保持其新鲜度的缓存不包含缓存失效,因此不在本讨论范围内。对于本文的其余部分,我们将假设存在缓存失效。

在某些情况下,缓存不一致几乎与数据库上的数据丢失一样严重。从用户的角度来看,它甚至无法与数据丢失区分开来。

可靠的一致性可观察性
为了解决缓存失效和缓存一致性,第一步涉及测量。我们想测量缓存的一致性,并在缓存中存在不一致的条目时发出警报。测量不能包含任何误报。人脑可以很容易地排除噪音。如果存在任何误报,人们会很快学会忽略它,并且该指标将失去信任并变得无用。我们还需要精确的测量,因为我们谈到要测量超过 10 个 9 的一致性。如果实现了一致性修复,我们希望确保我们可以定量地衡量它的改进。

这个庞大而复杂的分布式系统中,任何组件中的单个缺陷都可能导致缓存不一致,是否有可能找到一个引入大多数(如果不是全部)缓存不一致的地方? 
我们的任务变成了找到一个简单的解决方案来帮助我们管理这种复杂性。我们希望从单个缓存服务器的角度评估整个缓存一致性问题。归根结底,不一致必须在缓存服务器上实现。从它的角度来看,它只关心几个方面:

  • 它收到无效了吗?
  • 它是否正确处理了无效?
  • 之后项目变得不一致了吗?

我们构建了一个有状态的跟踪库,在这个紫色的小窗口中记录和跟踪缓存突变,所有有趣和复杂的交互都会触发导致缓存不一致的错误。它涵盖了缓存驱逐,即使没有日志也可以告诉我们无效事件是否永远不会到达。它嵌入到一些主要的缓存服务和整个失效管道中。它缓冲最近修改数据的索引,用于确定是否应记录后续缓存状态更改。它支持代码跟踪,因此我们将知道每个跟踪查询的确切代码路径。 
这种方法帮助我们发现并修复了许多缺陷。它提供了一种系统化且更具可扩展性的方法来诊断缓存不一致。事实证明它非常有效。