Derick
6479 words
32 minutes
如何使用缓存

开发人员至少对缓存的概念有些熟悉。毕竟,这种技术现在是如此普遍,从CPU到浏览器缓存,所有软件都在一定程度上依赖于缓存技术来提供快速响应。仅仅几毫秒的延迟就可能导致数百万的收入损失,因此亚毫秒级的响应正在成为常态。市场上有大量的缓存解决方案,因此选择正确的解决方案的过程本身就是一次冒险。

在这篇文章中,我们将讨论什么是缓存,以及缓存的好处。接下来,我们将讨论不同的缓存策略和缓存回收策略。最后,我们将回顾一些现有的可用缓存解决方案。

缓存基础知识#

什么是缓存?#

在软件开发中,缓存是一个组件,用于存储数据集的部分,这些数据集要么需要很长时间才能计算,要么来自另一个底层系统。通过减少不必要的计算并防止频繁使用数据的额外请求往返,可以提高应用程序性能或减少响应延迟。

缓存被设计为近实时地响应缓存请求,因此被实现为简单的键值存储。然而,内部工作方式仍然可以非常不同,并且取决于后端存储。

典型的用例是用于数据库的内存缓存或可缓慢检索的基于磁盘的数据,远程存储在慢速网络连接后面的数据或先前计算的结果。

什么是缓存命中和缓存未命中?#

当请求的数据在该高速缓存中已经可用并且可以在没有任何其他操作或处理的情况下返回时,发生缓存命中。

当请求的数据尚不可用,并且必须从底层系统检索数据或在返回数据之前计算数据时,就会发生缓存未命中。

应用程序挑战和缓存优势#

现代系统必须适应大量的流量,并需要快速响应。此外,随着流量和数据量的增加,应用程序需要能够增长才能成功。另一方面,大多数基础设施直接或间接地依赖于基于磁盘的数据库。

对于需要低延迟和可伸缩性的分布式应用程序来说,基于XML的数据库可能会带来很多挑战。一些最常见的挑战包括:

  • 缓慢的查询处理:从磁盘检索数据的速度,加上增加的查询处理时间,通常会使响应时间相对较高。有很多优化技术和设计可以提高查询性能。然而,在达到介质的物理限制之前,我们可以实现的性能是有限的。毕竟,数据库查询的大部分延迟是由从磁盘检索数据的物理过程决定的。
  • 可伸缩性成本:数据库可以水平或垂直扩展。这两种缩放技术都有其缺点。垂直扩展既昂贵,又可能达到可添加到机器的组件的物理限制。水平扩展允许对数据库进行分片以实现更高的吞吐量。尽管如此,扩展到更高的读取可能是昂贵的,并且可能需要大量的副本来实现。此外,我们必须非常小心,不要在试图实现更高的响应时间时变得不平衡。
  • 可用性:有时,与数据库服务器或数据库分片的连接可能会中断。如果没有缓存,系统将变得无响应,直到连接恢复。

缓存的总体好处是帮助内容消费者和内容提供者。一个好的缓存策略可以提供几个优点。

  • 提高响应速度:缓存可以提供更快的内容检索,并防止额外的网络往返。在用户附近维护的缓存(如浏览器缓存)可以使这种检索几乎是即时的。
  • 在相同的硬件上提高性能:对于内容起源的服务器,可以从相同的硬件中挤出积极的缓存。内容所有者可以利用更强大的服务器沿着传递路径来承担内容加载的主要任务。
  • 降低网络成本:根据缓存策略,内容可以在网络路径的多个区域中可用。这样,内容可以移动到更靠近用户的位置,并减少该高速缓存之外的网络活动。
  • 内容交付的稳健性:通过某些策略,即使由于网络短缺或服务器故障而无法使用缓存,也可以使用缓存向最终用户提供内容。
  • 消除数据库热点:在许多应用程序中,一小部分数据可能比其他数据更频繁地被访问。这可能会导致数据库中出现热点。热点可能需要基于最频繁使用的数据的吞吐量要求来过度供应数据库资源。在内存缓存中存储公共密钥可以减少过度配置的需求,同时提供快速和可预测的性能。

缓存什么数据#

一个很好的缓存指示器是找到任何元素,其中某个请求的多次执行将导致相同的结果。这包括数据库查询、HTML片段或繁重计算的输出。

一般来说,只有一个规则适用。数据不应该经常更改,但应该经常读取。

什么数据不应缓存#

易失性数据通常不适合缓存。每当数据发生变化时,必须使该高速缓存无效,并且根据我们使用的缓存策略,这可能是一个代价高昂的操作。

另一种不能从缓存中受益的数据类型是检索速度快的数据。缓存这些元素将在填充该高速缓存时引入额外的往返,并不可避免地增加所需的内存。缓存这些元素的好处可能不会显示预期的结果,从而使它们不值得开销。

缓存类型#

内存缓存#

内存中缓存是一块RAM,用于临时存储数据。由于访问RAM比访问其他介质(如硬盘驱动器或网络)要快得多,因此缓存可以帮助应用程序更快地运行,因为它可以更快地访问数据。

内存缓存的工作原理是首先留出一部分RAM用作该高速缓存。当应用程序尝试读取数据时,通常是从数据库等数据存储系统中读取数据,它会检查所需的记录是否已存在于该高速缓存中。如果是,那么应用程序将从该高速缓存读取数据,从而消除对数据库的较慢访问。如果所需的记录不在该高速缓存中,则应用程序从源读取该记录。当它检索该数据时,它还将数据写入该高速缓存,以便将来应用程序需要相同的数据时,它可以快速地从该高速缓存中获取数据。

内存缓存的一个广泛的用例是加速数据库应用程序,特别是那些执行许多数据库读取的应用程序。通过用从该高速缓存读取数据来替换一部分数据库读取,应用程序可以消除由于频繁的数据库往返而引起的延迟。这种用例通常出现在大量数据访问的环境中,例如在具有数据库动态内容的高流量网站中。

另一个用例涉及查询加速,其中对数据库的复杂查询的结果存储在该高速缓存中。运行分组和排序等操作的复杂查询可能需要大量时间才能完成。如果查询重复运行,就像许多用户访问的商业智能(BI)仪表板中的情况一样,将结果存储在缓存中将使这些仪表板具有更高的响应能力。

分布式缓存#

分布式缓存是一种系统,它将多台联网计算机的RAM集中到一个内存数据存储中,用作缓存,以提供对数据的快速访问。

虽然大多数缓存传统上存在于一个物理组件中,无论是服务器还是硬件组件,但分布式缓存可以通过链接多台计算机来获得更大的容量和处理能力,从而超越单个机器的物理限制。

8fee8oqs32mtpi8ihqdo.png

分布式缓存将多台计算机的RAM池化到单个内存中的数据存储中,用作数据缓存以提供对数据的快速访问。

分布式缓存在具有高数据量和负载的环境中特别有用。分布式体系结构允许通过向群集添加更多硬件来实现增量扩展,从而使该高速缓存能够随着数据增长而同步增长。

有几种应用程序可以将分布式缓存作为其架构的一部分的用例:

  • 应用加速:大多数应用程序都直接或间接地依赖于基于磁盘的数据库,因此无法始终满足当今日益苛刻的要求。通过在分布式缓存中缓存最频繁访问的数据,我们可以显著减少基于磁盘的系统的瓶颈。
  • 存储会话数据:站点可以将用户会话数据存储在缓存中,作为多个操作的输入,例如购物车和推荐。使用分布式缓存,我们可以拥有大量并发的Web会话,这些会话可以被系统中的任何服务器访问。这使我们能够将Web流量负载平衡到多个服务器,并且在任何应用服务器出现故障时不会丢失会话。
  • 极高的可扩展性:某些应用程序需要大量数据。通过在多台机器上利用更多资源,分布式缓存可以响应这些请求。

缓存数据访问策略#

当我们缓存数据时,我们可以从各种各样的缓存策略中进行选择,包括主动和被动的方法。我们选择实现的模式应该与我们的缓存和应用程序目标直接相关。

缓存(延迟加载)#

缓存可能是最常用的缓存方法。此策略规定该高速缓存必须位于旁边,应用程序将直接与该高速缓存和数据库通信。

在此策略中,当应用程序需要某些数据时,它将首先查询该高速缓存。如果该高速缓存包含该元素,则我们有一个高速缓存命中,并且该高速缓存将数据返回给应用程序。如果数据不存在于该高速缓存中,则表示缓存未命中。应用程序现在必须做一些额外的工作。应用程序首先必须在数据库中查询所需的数据。然后,它将数据返回给客户端,最后用检索到的数据更新该高速缓存。现在,对同一数据的任何后续读取都将导致缓存命中。

br60vgp346e9n9uru903.png

缓存旁缓存通常是通用的,最适合读取繁重的工作负载。

使用缓存的系统对缓存故障具有弹性。如果有多个缓存节点,并且一个节点发生故障,它不会导致连接的全部原因,但应用程序可能会面临增加的延迟。随着新的缓存节点上线,更多的请求被重定向到它们,节点将在每次缓存未命中时填充所需的数据。在整个缓存失败的情况下,应用程序仍然可以通过数据库请求访问数据。

这种策略的缺点是在缓存未命中之后需要三次网络往返。首先,应用程序需要检查该高速缓存。接下来,应用程序需要从数据库中检索数据。最后,应用程序需要更新该高速缓存。这些往返可能会导致响应的明显延迟。

Read Through #

与Cache Aside相比,Read Through将获取值的责任从Cache转移到该高速缓存提供程序。此策略要求该高速缓存必须位于应用程序和数据库之间。

在此策略中,当应用程序需要某些数据时,它将查询该高速缓存。如果该高速缓存包含该元素,则我们有一个高速缓存命中,并且该高速缓存将数据返回给应用程序。如果数据不存在于该高速缓存中,则表示缓存未命中。该高速缓存首先必须查询数据库以获得所需的数据。之后,该高速缓存将用所需数据更新自身。最后,该高速缓存将检索到的数据返回给应用程序。现在对同一数据的任何后续读取都将导致缓存命中。

i77mkvey94qmqebjgb2g.png

当我们有数据要保存在该高速缓存中以供频繁读取时,即使这些数据定期更改,读通缓存也是与写通策略配合使用的理想选择。

此策略不适用于应用程序中的所有数据访问。如果系统将该高速缓存用于数据库和应用程序之间的所有数据访问,则缓存故障可能导致应用程序性能瓶颈,或者由于应用程序无法访问数据库而直接导致应用程序崩溃。

写入#

与Read Through类似,但对于写入,Write Through将写入责任转移到该高速缓存提供程序。此策略要求该高速缓存必须位于应用程序和数据库之间。此策略不提供任何从主数据源阅读数据的功能,但处理应用程序发出新数据或更新时发生的情况。

在此策略中,当应用程序尝试更新现有数据或向缓存中添加新数据时,它将命中该高速缓存。此操作总是导致缓存命中,并且该高速缓存将更新其条目或为数据创建新条目。然后,该高速缓存将更新主数据存储。最后,该高速缓存将确认数据已成功存储。

55dsim7gs0yewzq06jo2.png

就其本身而言,Write Through似乎做得不多,如果事实上,它引入了额外的写入延迟,因为数据首先写入ache,然后写入数据库。但是,当此策略与Read Through阅读策略配对时,可确保数据一致性。

与Read Through策略一样,此策略不适用于应用程序中的所有数据访问。如果系统将该高速缓存用于数据库和应用程序之间的所有数据访问,则缓存故障可能导致应用程序性能瓶颈,或者由于应用程序无法访问数据库而直接导致应用程序崩溃。

Write Around #

写回缓存具有与直写缓存类似的功能。在写回缓存该高速缓存数据将被更新,只有当它已经映射到该高速缓存,并同时写入数据“通过”到后端存储。

在此策略中,当应用程序尝试添加或更新某些数据时,它将查询该高速缓存。该高速缓存将用更新的数据更新后端存储。然后,如果该高速缓存包含更新数据的条目,则它将更新自身,否则,它将完全跳过数据。

mg31m8uyogqd951m6wyv.png

此策略保护该高速缓存不被不那么频繁读取的数据淹没,同时为写入操作引入最小延迟。它还确保了该高速缓存和主数据存储之间的数据一致性。因此,它非常适合具有非常高的一次性数据写入量的系统,例如来自聊天应用程序的消息。

这种策略的缺点是,最近写入的数据总是会导致缓存未命中(因此延迟更高),因为数据只能在较慢的后端存储中找到。

Write Back #

回写的工作原理与直写策略非常相似。主要区别在于,该高速缓存不同步更新主数据存储,而是在预定义的时间间隔后分批更新。

在此策略中,当应用程序添加或更新数据时,它会与案例进行通信。案例将向应用程序发送确认。在定义的时间间隔之后,该高速缓存将对数据存储执行批量查询并更新所有相关数据。

3vsci7om1cdx3bd4oh7s.png

回写缓存提高了写入性能,是读取和写入繁重工作负载的理想选择。

由于应用程序只写入缓存服务,因此它不需要等待数据写入基础数据源,从而提高了性能。此外,由于所有的阅读和写都是在该高速缓存上执行的,因此应用程序不会出现数据库故障。如果数据库失败,仍可以访问排队的项目。

这一战略也带来了一些需要解决的问题。考虑到该策略首先读取和写入该高速缓存,在该高速缓存和主数据存储之间仅存在最终一致性。如果主数据存储与其他应用程序共享,则如果其他应用程序的读取操作发生在批处理操作之间,则它们总是有获得陈旧数据的危险。此外,无法知道缓存更新是否会与其他外部更新冲突。这必须手动或手动地处理。

Eviction Policies #

回收策略允许该高速缓存确保其大小不超过最大限制。为了实现这一点,根据驱逐策略从缓存中删除现有元素,但可以根据应用程序的要求进行自定义。

缓存解决方案可能与不同的回收策略兼容,但在选择缓存策略之前,最好了解应用程序可能需要什么回收策略。

Least Recently Used (LRU)#

最常用的策略之一是最近最少使用。Least Recently Used eviction(最近最少使用的清除)策略会删除在时间上最早使用的值。为了进行分析,该高速缓存中的每个记录都跟踪其最后一次访问时间戳,该时间戳可以与其他记录进行比较,以找到最近最少使用的项。

secea16e7ey625tecmeu.png

Least Frequently Used (LFU)#

最不常用驱逐策略会删除访问次数最少的值。为了进行分析,每个记录使用仅递增的计数器来跟踪其访问。然后可以将该计数器与其他记录的计数器进行比较,以找到最不常用的元素。

yh0cfk9kud0bt9l4j56z.png

Most Recently Used (MRU)#

Most Recently Used eviction(最近使用的回收)策略将删除在时间上最近使用的值。为了进行分析,每个记录都会跟踪其最后一次访问的时间戳,可以将其与其他记录进行比较,以找到最近使用的元素。

secea16e7ey625tecmeu.png

Most Frequently Used (MFU)#

最频繁使用的驱逐策略删除访问次数最多的值。为了进行分析,每个记录使用仅递增的计数器来跟踪其访问。然后,可以将此计数器与这些记录进行比较,以查找最常用的元素。

yh0cfk9kud0bt9l4j56z.png

Least Time To Live (LTTL)#

最短生存时间驱逐策略删除TTL字段中时间最短的值。要进行此分析,每个记录都要跟踪其TTL,该TTL是在将其添加到该高速缓存时分配的,并以特定的时间间隔递减。然后可以将该字段与其他记录的字段进行比较,以找到在该高速缓存中生存时间最长的项。

ygguyemtr83z07zby3bc.png

Random #

Random eviction策略随机删除值。此策略不考虑项在该高速缓存中的插入顺序,也不考虑访问项的频率。当我们有循环访问并且所有元素都被连续扫描时,或者我们期望分布是均匀的时,可以使用此策略。

缓存中间件#

Memcached#

Memcached是一个通用的分布式内存缓存系统。它通常用于动态数据库驱动的网站,以减少必须读取外部数据源的次数。Memcached是免费和开源的,在修订的BSD许可证下授权,使其成为一个非常低成本的解决方案。

该系统被设计为一个简单的键值存储。Memcached不理解应用程序正在保存什么-它可以将字符串和对象存储为值,而键必须始终存储为字符串。

在分布式环境中,Memcached节点不会相互交互,因为系统不提供任何同步或复制功能。因此,客户端必须决定哪个节点必须访问特定的数据集。值得注意的Memcached用户包括YouTube,Reddit,Twitter和维基百科。

Limitations#

Memcached不为该高速缓存条目提供任何持久性,因此每次崩溃或重新启动时,该高速缓存都需要再次预热。

另一个限制是值的大小限制。每个键的值必须最多为1 MB。这意味着大型对象或数据集可能会占用更多空间,并且必须在不同的缓存插槽中进行分段。此外,对象在存储到该高速缓存中之前必须被序列化,从而增加了阅读/写操作的延迟。

Redis#

Redis(Remote Dictionary Server)是一个内存数据结构存储,用作分布式内存键值数据库,缓存和消息代理。

Redis支持多种数据结构,即列表,集合,排序集合和字符串。它还支持范围查询、超级日志和地理空间索引。

Redis通常将整个数据集保存在内存中,但可以配置为通过两种不同的方法持久化其数据。第一种方法是通过快照,其中数据集以二进制转储的形式定期从内存异步传输到磁盘。第二种方法是日志记录,其中将修改数据集的每个操作的记录添加到后台进程中的仅追加文件中。

默认情况下,Redis至少每2秒向文件系统写入一次数据,如果需要,可以提供或多或少的健壮选项。在默认设置下发生系统故障的情况下,只有几秒钟的数据会丢失。

Redis还支持主副本复制。来自任何Redis服务器的数据都可以复制到任意数量的副本。Redis还提供了一个订阅的特性,所以一个副本的客户端可以订阅一个频道,并接收发布给主服务器的完整消息。

Limitations #

在分布式环境中,Redis根据分配给每个master的hash slot对数据进行分片。如果任何主机出现故障,将在此插槽上写入的数据将丢失。此外,不支持故障转移,除非主设备至少有一个从设备。

由于Redis将数据存储在内存中的大型哈希表中,因此需要大量的RAM。

Aerospike #

Aerospike是一个闪存和内存中的开源分布式键值NoSQL数据库管理系统。Aerospike最重要的卖点之一是支持混合内存模型-这意味着如果RAM用完,其他合适的闪存驱动器(如SSD,NVMes等)可以用作替代品。

Aerospike使用闪存驱动器进行垂直扩展。IOPS(每秒输入输出)持续增长。SSD可以在每个节点上存储比DRAM多几个数量级的数据,NVMe驱动器现在可以在每个驱动器上执行高达100 K的IOPS。Aerospike利用了这些功能,它可以在亚毫秒级的延迟下每秒执行数百万次操作。

Hazelcast#

Hazelcast是一个基于Java的开源内存数据网格。在Hazelcast网格中,数据均匀地分布在集群的节点之间,允许处理和可用存储的水平扩展。备份也分布在节点之间,以防止故障或任何单个节点。

Hazelcast可以在本地、云端和Docker容器中运行。Hazelcast提供多种云配置和部署技术的集成,包括Apache jclouds、Consul、eruaka、Kubernetes和Zookeeper。Hazelcast还可以使基于云的节点或本地节点能够自动发现彼此。

最后的想法#

在这篇文章中,我们讨论了什么是缓存以及缓存的好处。我们还研究了什么是内存缓存和分布式缓存。我们回顾了最常见的缓存策略,并讨论了最流行的驱逐策略。最后我们回顾了一些可用的缓存提供程序。

现在应该更加清楚的是,选择缓存服务不仅是在知名度或熟悉度之间做出决定,而且更多的是应用程序的用例,数据访问模式,基础设施要求,可用性和数据量等。

如何使用缓存
https://blog.ithuo.net/posts/how-to-use-cache/
Author
Derick
Published at
2022-12-12