缓存

缓存是一种常见的技术,目标是提高系统的性能和伸缩性。 为此,它会暂时会经常访问的数据复制到位置靠近应用程序的快速存储。 如果这种快速数据存储比原始源更靠近应用程序,则缓存可以通过更快速提供数据,大幅改善客户端应用程序的响应时间。

如果客户端实例重复读取同一数据,则缓存是最有效的方式,尤其是原始数据存储存在以下情况时:

  • 保持相对静态。
  • 相对于缓存速度而言较慢时。
  • 受限于激烈的资源争用。
  • 由于距离遥远,网络延迟会造成访问速度缓慢。

分布式应用程序中的缓存

在缓存数据时,分布式应用程序通常会实施以下一种或两种策略:

  • 使用专用缓存,其中的数据保存在运行应用程序或服务实例的计算机本地。
  • 使用共享缓存,充当可由多个进程和/或计算机访问的公用源。

在这两种情况下,缓存可在客户端和/或服务器端执行。 通过为系统提供用户界面(例如 Web 浏览器或桌面应用程序)的进程来实现客户端缓存。 通过远程运行的提供业务服务的进程来实现服务器端缓存。

专用缓存

最基本类型的缓存是内存中存储。 这种缓存保留在单个进程的地址空间中,可由该进程中运行的代码直接访问。 此缓存类型可进行非常快速的访问。 此外,还可提供极其有效的方式用于存储适度的静态数据量,因为缓存大小通常受限于托管进程的计算机上可用内存量。

如果缓存的信息需要超过内存中实际可用的信息,可以将缓存数据写入本地文件系统。 这比访问保留在内存中的数据更慢,但应该仍比通过网络检索数据更快速且更可靠。

如果有多个并行运行的、使用此模型的应用程序实例,则每个应用程序实例将有自身的独立缓存用于保存自身的数据副本。

应该将缓存视为过去某个时间点原始数据的快照。 如果此数据不是静态的,则有可能不同的应用程序实例会在其缓存中保存不同版本的数据。 因此,这些实例执行的同一查询可能会返回不同的结果,如图 1 所示。

图 1:在不同的应用程序实例中使用内存中缓存

共享缓存

使用共享缓存有助于缓解每个缓存中可能存在不同数据的忧虑,这种情况可能会发生于内存中缓存。 共享缓存可确保不同的应用程序实例看到同一缓存数据视图。 为此,缓存将定位在不同的位置,通常作为不同服务的一部分托管,如图 2 所示。

图 2:使用共享缓存

伸缩性是共享缓存的一个重要优势。 许多共享缓存服务是使用服务器群集实施的,并以透明方式利用将数据分散到群集的软件。 应用程序实例只会将请求发送到缓存服务。 底层基础结构负责确定缓存数据在群集中的位置。 可以轻松地通过添加更多服务器来扩展缓存。

共享缓存方法有两个主要缺点:

  • 缓存的访问速度较慢,因为它不再保留在每个应用程序实例的本地。
  • 为了满足实施不同缓存服务的要求,可能会增大解决方案的复杂性。

使用缓存时的注意事项

以下部分更详细地说明了设计和使用缓存时的注意事项。

确定何时缓存数据

缓存可大幅提高性能、伸缩性和可用性。 当数据越多且需要访问此数据的用户越多,缓存的优点也就越大。 这是因为在原始数据存储中处理大量并发请求时,可以减少相关的延迟和争用。

例如,数据库可以支持有限数目的并发连接。 但从共享缓存而不是底层数据库检索数据可让客户端应用程序访问此数据,即使当前可用的连接数已用尽。 此外,如果数据库变得不可用,客户端应用程序也许可以使用缓存中保存的数据继续运行。

考虑使用经常读取但很少修改的缓存(数据读取操作的比例要高于写入操作)。 但是,不应将缓存用作关键信息的权威存储。 应确保应用程序不可丢失的所有更改始终存储到永久性数据存储中。 这意味着,当缓存不可用时,应用程序仍可以使用数据存储继续操作,用户不会丢失重要信息。

确定如何有效缓存数据

有效使用缓存的关键在于确定最适合缓存的数据,以及最适合缓存的时间。 数据可能在第一次由应用程序检索时随选添加到缓存。 这意味着,应用程序仅需从数据存储检索一次数据,而后续访问可通过使用缓存来满足。

或者,可以事先在缓存中部分或完全填充数据,这通常发生在应用程序启动时(此方法称为种子设定)。 但是,不建议对大型缓存实施种子设定,因为这种方法在应用程序开始运行时,可能会在原始数据存储上造成突发性的高负载。

使用模式分析通常可以帮助确定是否要完整或部分预先填充缓存,以及选择要缓存的数据。 例如,对于定期(也许是每天)使用应用程序的客户,使用静态用户配置文件数据设定缓存种子可能相当实用,但不适用于一周仅使用一次应用程序的客户。

缓存通常适用于不会变化或很少变化的数据。 示例包含引用信息,例如电子商务应用程序中的产品和价格信息,或构建成本高昂的共享静态资源。 此数据的部分或全部可在应用程序启动时加载到缓存,以便将资源需求降到最低并提高性能。 拥有定期更新缓存中引用数据的后台进程可能也是适当的方式,可确保其处于最新状态,或在引用数据更改时刷新缓存。

缓存可能较不适合动态数据,但这种考虑因素有一些例外情况(请参阅本文后面的“缓存高动态数据”部分以了解详细信息)。 如果原始数据定期更改,缓存的信息可能很快就会过时,或者为了保持与原始数据存储的缓存同步而产生开销,导致降低缓存的效率。

请注意,缓存中不一定会包含实体的完整数据。 例如,如果数据项代表多值对象(例如具有名称、地址和帐户余额的银行客户),则其中某些元素可以保持静态(例如名称和地址),而有些元素(例如帐户余额)则可能更加动态。 在这种情况下,缓存数据的静态部分,并只在需要时检索(或计算)剩余信息可能相当有用。

建议执行性能测试和使用情况分析来确定缓存的预先填充和/或按需加载是否适当。 这种判断应该基于数据易变性和使用模式。 在会遇到重度负载且必须高度可缩放的应用程序中,缓存利用和性能分析特别重要。 例如,在高度可缩放的方案中,有时可以设定缓存种子,以在高峰期降低数据存储的负载。

缓存还可用于在应用程序运行时避免重复计算。 如果操作会转换数据或执行复杂计算,则可以在缓存中保存操作的结果。 如果后续需要相同的计算,应用程序只需从缓存中检索结果。

应用程序可以修改保存在缓存中的数据。 但是,我们建议将缓存视为可能随时消失的暂时性数据存储。 请勿只在缓存中存储重要数据,而是确保同时在原始数据存储中保留信息。 这意味着,当缓存不可用时,可以最大程度地减少数据丢失。

缓存高动态数据

在永久性数据存储中存储快速变化的信息时,可能会给系统造成开销。 例如,假设有一个会持续报告状态或其他度量的设备。 在缓存信息几乎一直处于过期状态的情况下,如果应用程序选择不要缓存此数据,则在数据存储中存储和检索此信息时,同样存在这种考虑因素。 在保存和提取此数据时它可能已经更改。

在这种情况下,请考虑直接在缓存而不是永久性数据存储中存储动态信息的优点。 如果数据不太重要且不需要审核,则偶尔丢失更改的数据就无关紧要。

管理缓存中的数据过期

在大多数情况下,缓存中保存的数据是保存在原始数据存储中的数据的副本。 原始数据存储中的数据可能在缓存后更改,导致缓存的数据过时。 许多缓存系统允许将缓存配置为使数据过期,以及减少数据可以过期的时间长短。

过期的缓存数据将从缓存中删除,应用程序必须从原始数据存储中检索数据(它可以将新提取的信息放回缓存)。 在配置缓存时,可以设置默认的过期策略。 在许多缓存服务中,当以编程方式将单个对象存储在缓存中时,还可以规定这些对象的过期时间。 某些缓存可让你将过期时间指定为绝对值,或者,如果并未在指定的时间内访问,则从缓存中删除项的滑动值。 此设置将重写任何缓存范围的过期策略,但只适用于指定的对象。

 备注

请慎重考虑缓存的过期时段及其包含的对象。 如果设置的时段太短,则对象很快就会过期,因此就减少了使用缓存带来的优势。 如果设置的时段太长,则会面临数据过时的风险。

此外,如果允许数据长时间驻留,则缓存有可能会填满。 在此情况下,将新项添加到缓存的任何请求可能会导致某些项被强行删除,这个过程称为逐出。 缓存服务通常按最近最少使用 (LRU) 原则逐出数据,但通常可以替代此策略并防止逐出项。 但是,如果采用这种方法,则会面临缓存超过可用内存。 应用程序尝试将项添加到缓存时会失败并发生异常。

某些缓存的实施可能会提供其他逐出策略。 有多种类型的逐出策略。 其中包括:

  • 最近使用的策略(预期不再需要数据)。
  • 先进先出策略(先逐出最旧的数据)。
  • 或基于触发事件显式删除(例如,正在修改数据)。

使客户端缓存中的数据失效

保存在客户端缓存中的数据通常被视为不受向客户端提供数据的服务的支持。 服务不能直接强制客户端添加或删除来自客户端缓存的信息。

这意味着,使用配置不当的缓存的客户端可能继续使用过时的本地缓存信息。 例如,如果未正确实施过期策略,当原始数据源中的信息已更改时,客户端可能使用本地缓存的已过时信息。

如果要构建通过 HTTP 连接提供数据的 Web 应用程序,可以隐式强制 Web 客户端(例如浏览器或 Web 代理)提取最新的信息。 当资源通过更改该资源的 URI 更新时,可以执行此操作。 Web 客户端通常使用资源的 URI 作为客户端缓存中的键,因此更改 URI 会导致 Web 客户端忽略任何先前缓存的资源版本,并改为提取新的版本。

管理缓存中的并发

缓存通常设计为由应用程序的多个实例共享。 每个应用程序实例可以读取和修改缓存中的数据。 因此,任何共享数据存储中会出现的并发问题,在缓存中同样也会出现。 在应用程序需要修改缓存中保存的数据的情况下,可能需要确保应用程序的一个实例所做的更新不会盲目地覆盖另一个实例所做的更改。

根据数据的性质和冲突的可能性,可以采用以下两种并发方式之一:

  • 乐观并发。 应用程序检查以确定缓存中的数据自检索之后、更新之前是否已更改。 如果数据保持相同,则可以进行更改。 否则,应用程序必须确定是否要进行更新。 (促使做出此决定的业务逻辑特定于应用程序。)这种方法适合不常更新或不太可能发生冲突的情况。
  • 悲观并发。 应用程序在检索缓存中的数据时锁定数据,以避免另一个实例更改数据。 此过程可确保不发生冲突,但可能阻止其他需要处理同一数据的实例。 悲观并发可能会影响解决方案的伸缩性,建议只对短期操作使用。 这种方法可能适用于很可能发生冲突的情况,特别是当应用程序更新缓存中的多个项,且必须确保这些更改一致应用时。

实现高可用性和伸缩性并提高性能

避免使用缓存作为数据的主存储库;主存储库应该是从中填充缓存的原始数据存储的角色。 原始数据存储负责确保数据的持久性。

请小心不要将共享缓存服务可用性的重要依赖性引入解决方案。 如果提供共享缓存的服务不可用,应用程序应能继续工作。 应用程序应该不会在等待缓存服务恢复时停止响应或失败。

因此,应用程序必须准备好检测缓存服务的可用性,并在无法访问缓存时回退到原始数据存储。 断路器模式可用于处理这种情况。 提供缓存的服务可以恢复,当该服务可用后,缓存会在从原始数据存储读取数据时,遵循缓存端模式等策略重新填充。

但是,在缓存暂时不可用的情况下应用程序回退到原始数据存储可能会影响系统的伸缩性。 在恢复数据存储时,原始数据存储可能忙于处理数据请求,导致超时和连接失败。

考虑在每个应用程序实例中实施本地专用缓存,以及所有应用程序实例访问的共享缓存。 当应用程序检索项时,可能会先后在本地缓存、共享缓存和原始数据存储中检查。 共享缓存不可用时,本地缓存可以使用共享缓存或数据库中的数据来填充。

采用此方法需要经过慎重的配置,以防止本地缓存相对于共享缓存而言太过时。 但在无法访问共享缓存时,它可以充当缓冲区。 图 3 显示了此结构。

图 3:将本地、专用和共享缓存配合使用

为了支持保存相对长期数据的大型缓存,某些缓存服务在缓存不可用时,提供实施自动故障转移的高可用性选项。 这种方法通常涉及到将存储在主缓存服务器上的缓存数据复制到辅助缓存服务器,并在主服务器故障或断开连接时切换到辅助服务器。

为了减少与写入多个目标相关的延迟,当数据写入主服务器上的缓存时,复制到辅助服务器的操作可以异步发生。 此方法可能会导致某些缓存的信息在发生故障时丢失,但是此数据的比例应该小于缓存的总体大小。

如果共享缓存很大,则在节点上分区缓存数据可能很有帮助,这可减少争用的可能性,并提高伸缩性。 许多共享缓存支持动态添加(与删除)节点,以及重新平衡分区之间的数据的功能。 这种方法可能涉及到群集,其中,节点集合将作为无缝单一缓存向客户端应用程序呈现。 但在内部,数据分散在节点之间并遵循某种预定义的分配策略,以便平均地平衡负载。

群集还可以提高缓存的可用性。 如果节点发生故障,仍可访问缓存的剩余部分。 群集经常与复制和故障转移结合使用。 每个节点都可复制且副本在节点故障时可快速联机。

许多读取和写入操作可能会涉及到单个数据值或对象。 但是,有时可能需要快速存储或检索大量数据。 例如,设定缓存种子可能涉及到将数百或数千个项写入到缓存。 应用程序还可能需要从缓存中检索属于同一请求的大量相关项。

许多大型缓存针对这些目的提供了批处理操作。 这样,客户端应用程序便可以将大量的项打包成单个请求,并减少执行大量小型请求时的相关开销。

缓存和最终一致性

要使缓存端模式正常工作,填充缓存的应用程序实例必须有权访问最新且一致的数据版本。 在实施最终一致性的系统(例如复制的数据存储)中,情况可能不是这样。

应用程序的一个实例可以修改数据项,使该项的缓存版本失效。 应用程序的另一个实例可以尝试从导致缓存未命中的缓存读取此项,因此它将从数据存储中读取数据,并将它添加到缓存。 但是,如果数据存储没有完全与其他副本同步,则应用程序实例可能会使用旧值来读取并填充缓存。

 

保护缓存的数据

无论使用的缓存服务为何,都应该考虑如何防范缓存中保存的数据遭到未经授权的访问。 有两个主要考虑因素:

  • 缓存中数据的隐私性。
  • 数据在缓存与使用缓存的应用程序之间流动时的隐私性。

若要保护缓存中的数据,缓存服务可以实施一种身份验证机制,要求应用程序指定:

  • 哪些标识可以访问缓存中的数据。
  • 允许这些标识执行的操作(读取和写入)。

为了减少读取和写入数据时的相关开销,当标识已获得写入和/或读取缓存的权限时,该标识可以使用缓存中的任何数据。

如果需要限制对缓存数据子集的访问权限,可以执行以下操作之一:

  • 将缓存拆分成分区(使用不同的缓存服务器),并只向标识授予他们有权使用的分区的访问权限。
  • 使用不同的密钥来加密每个子集中的数据,并只向应该具有每个子集访问权限的标识提供加密密钥。 客户端应用程序可能仍然能够检索缓存中的所有数据,但它只能够解密具有密钥的数据。

还必须在数据流入或流出缓存时保护数据。 为此,可以依赖于客户端应用程序用来连接缓存的网络基础结构所提供的安全功能。 如果在托管客户端应用程序的同一组织中使用现场服务器来实施缓存,则网络本身的隔离可能不需要用户采取任何其他措施。 如果缓存位于远程,且需要基于公共网络(例如 Internet)的 TCP 或 HTTP 连接,请考虑实施 SSL。

看了这么久,若觉得有点累,可以到找一下音乐或电影网站放松一下心情,大家可以到百度影集(http://www.baidumovies.com/)这个网站看一下有没有自己喜欢的电影或电视剧或娱乐信息,这个网站信息更新比较快,电影也较多,目前收藏有20000多部国外内电影或电视剧。

原文地址:https://www.cnblogs.com/BlogNetSpace/p/1553617.html