Distributed Caching:

掌握分布式应用程序中的缓存(上)_应用程序

如果每次我在软件系统的缓存实现中遇到一个错误都能赚到一美元的话……我大概可以支付Redis Enterprise一年的企业订阅费用了。

缓存,似乎是这样一种东西,你几乎能把它做对,但永远不会完全对。这是有充分理由的。毕竟——缓存(或者更确切地说是缓存失效)是计算机科学中被认为最难解决的两个基础性问题之一。当然,另一个是命名变量。

无论是开玩笑还是认真——缓存确实很难做到特别是对于大规模分布式应用来说。因此,团队通常会经历一个迭代和实验的过程,以调整他们的缓存策略和实现——直到希望在某个时候,他们能够将其调整到一个合理且半优化的状态。

在这篇文章中,我想揭开缓存的一些常见误解和未被注意到的方面的面纱。

希望在阅读完这篇文章后,您能对缓存是什么、主要的缓存方法、需要注意的事项以及各种缓存技术在实际应用中的应用有一个更清晰的理解。

那么,事不宜迟……

什么是缓存?

简而言之,缓存是一种将数据存储在一个临时介质中的行为,这样数据的检索成本更低、更快或更优化,而不是从其原始存储(记录系统)中检索。

换句话说,想象以下用例。

一个订单管理系统需要从库存系统中检索产品信息。假设库存系统性能不佳。每次它收到请求时,都必须访问一个集中式数据库来获取产品信息。那个数据库很慢,不能支持太多并行请求。

掌握分布式应用程序中的缓存(上)_应用程序_02

没有缓存

为了提高性能并减轻库存数据库的负担,我们引入了一个缓存层,现在我们将同样的产品信息存储在这里。但现在,不再去访问那个笨重的数据库的库存系统,我们首先访问缓存,如果数据在缓存中,我们就从那里获取。

掌握分布式应用程序中的缓存(上)_分布式缓存_03

有缓存

我们在这里做的是引入了一个临时存储介质(缓存)来提高性能并优化原始库存数据库的资源使用。

什么构成了“缓存”?

人们开始困惑的一点是关于缓存的技术性质。

在软件开发领域的大多数人听到“缓存”这个词时都有非常具体的联想。我们通常将这个词与分布式缓存产品(如Redis、Memcached或EHCache)联系在一起。有时,我们会想到浏览器缓存、数据库缓存、操作系统缓存,甚至硬件缓存。

这正是重点。缓存的概念并不限于计算机科学领域中的某个特定产品或范围。从广义上讲,“缓存”实际上是任何类型的临时介质,我们从某个记录系统中复制数据到这里。我们这样做是因为以某种方式将数据存储在临时介质中更有利。

典型的原因是由于缓存比原始存储的成本更低、性能更好或可扩展性更高。

如果我们看订单管理和库存系统的前例,缓存层理论上可以是以下几种:

  1. 分布式缓存产品(例如Redis)
  2. 另一个有自己数据库的微服务
  3. 实际库存管理系统中的内存存储

以上所有选项都符合作为缓存的标准,尽管每种实现方式在不同选项之间有所不同。

简而言之——以上所有这些都可以是缓存。缓存,作为一个概念,实际上在计算机系统的各个层次和许多数字领域中实现。

一些术语

在继续之前,了解与缓存相关的不同术语是很重要的。

记录系统:存储数据的永久存储。很可能是数据库。也称为真相系统。

缓存未命中:当应用程序查询缓存但该特定记录在缓存中不存在时。

缓存命中:当记录在缓存中存在并返回时。

缓存污染:当缓存中充满了未被使用或查询的值时。

缓存驱逐:从缓存中删除条目以释放其内存的过程。

数据新鲜度:缓存中的记录与底层记录系统的同步程度。

缓存过期:作为驱逐过程的一部分或作为我们将讨论的缓存失效的一部分,根据时间删除缓存记录。

现在我们已经充分掌握了缓存术语,让我们深入探讨缓存可能实现的一些地方和层次。

缓存在哪些地方实现?

正如我们已经提到的,缓存在整个技术领域——在所有层次和许多不同的技术堆栈中使用。

在硬件层面,缓存作为CPU架构的一部分使用,例如,以L1-L3(一级、二级、三级)缓存的形式。

在操作系统内核层面,有一种磁盘缓存形式,称为页面缓存。还有其他形式。

在基于Web的系统中,当然有浏览器缓存和CDN(内容分发网络)。这种缓存通常用于客户端或CDN侧的静态资源(图像、样式表等)。目的是减少带宽并以更快、更高效和更低的成本将这些资源提供给用户。

不同类型的应用程序和中间件也有自己的缓存。例如,数据库使用缓存来保存常用的查询以及频繁返回的结果集。

当然,还有许多强大的软件缓存产品,例如Redis、EHCache、Memcached、Hazelcast、Infinispan等,允许在分布式应用程序中实现可扩展的分布式缓存。

需要强调的一点是,“分布式”缓存的概念可以与“本地”或“局部化”缓存进行对比。分布式缓存是一种缓存形式,分布在多个设备上通过网络连接。本地缓存只存在于一个设备上。

理解这两个概念的最好方法是想象一个应用程序部署在一组服务器中。换句话说,有多个实例同时运行该应用程序,这对于任何从事大规模应用程序工作的人来说是很熟悉的。

如果我们在这样的系统中引入一个分布式缓存,任何一个应用程序实例都可以访问该缓存并有能力修改其记录。

另一方面,对于本地缓存,每个实例都有自己的缓存——最有可能在该特定实例的内存中。不同的实例无法访问另一个实例的缓存。它们只能访问自己的缓存。

这两种方法各有优缺点。

一方面,如果你有多个实例访问一个缓存——你可能需要解决同步问题、竞争条件、数据损坏以及分布式应用程序所带来的其他挑战。另一方面,共享缓存是一个强大的概念,因为它允许应用程序处理本地(尽管更简单)缓存无法实现的用例。

例如,你可能将你的应用程序部署在云环境中的多个可用区中。每个可用区可能有一个运行你的应用程序的VM实例集群。这些集群中的每一个很可能有自己的分布式缓存。原因之一是分布式缓存的一个前提是能够快速有效地访问它。这意味着缓存与它所服务的实例之间具有网络接近性(不仅是物理上的,而且是虚拟上的)。

同时,有一些挑战是分布式缓存和本地缓存都共有的。

主要挑战当然是保持数据新鲜度、优化缓存失效和驱逐以及根据特定用例管理缓存的方法之间的持续平衡。

这个缓存管理的非常重要的概念——缓存模式是我们接下来要讨论的内容。

如同软件工程中的大多数决策一样,每种方法都有其权衡——换句话说,各有优缺点。我们将在下面讨论每种方法的优缺点。

缓存是软件工程师和软件架构师需要理解的一个关键概念。然而,这远不是唯一的重要概念。