Understanding the Cache Abstraction

Cache vs Buffer

术语“`buffer`" 和 “`cache`" 往往可以互换使用。但请注意,它们代表着不同的事物。传统上,缓冲区用作快速实体和慢速实体之间的中间临时数据存储。由于一方必须等待另一方(这会影响性能),因此缓冲区通过允许整个数据块一次移动而不是分小块移动来缓解这种情况。数据只从缓冲区写入和读出一次。此外,缓冲区至少对一个知道它的参与者是可见的。

The terms, “buffer” and “cache,” tend to be used interchangeably. Note, however, that they represent different things. Traditionally, a buffer is used as an intermediate temporary store for data between a fast and a slow entity. As one party would have to wait for the other (which affects performance), the buffer alleviates this by allowing entire blocks of data to move at once rather than in small chunks. The data is written and read only once from the buffer. Furthermore, the buffers are visible to at least one party that is aware of it.

另一方面,缓存是通过定义进行隐藏的,双方都不会意识到正在进行缓存。它也会提高性能,但它让相同的数据能够通过高速方式多次读取。

A cache, on the other hand, is, by definition, hidden, and neither party is aware that caching occurs. It also improves performance but does so by letting the same data be read multiple times in a fast fashion.

您可以在 here找到缓冲区和缓存之间差异的进一步说明。

You can find a further explanation of the differences between a buffer and a cache here.

缓存抽象的核心是将缓存应用到 Java 方法,从而根据缓存中可用信息减少执行次数。也就是说,每次调用目标方法时,抽象都会应用一种缓存行为,检查是否已为给定参数调用了该方法。如果已经调用,则返回缓存结果,而无需调用实际方法。如果尚未调用该方法,则调用该方法,并将结果缓存在返回给用户,以便在下一次调用该方法时返回缓存结果。通过这种方式,仅针对给定参数集调用昂贵的方法(无论是 CPU 绑定还是 IO 绑定),并且可以重复使用结果,而无需实际再次调用该方法。缓存逻辑应用得透明,对调用方没有干扰。

At its core, the cache abstraction applies caching to Java methods, thus reducing the number of executions based on the information available in the cache. That is, each time a targeted method is invoked, the abstraction applies a caching behavior that checks whether the method has been already invoked for the given arguments. If it has been invoked, the cached result is returned without having to invoke the actual method. If the method has not been invoked, then it is invoked, and the result is cached and returned to the user so that, the next time the method is invoked, the cached result is returned. This way, expensive methods (whether CPU- or IO-bound) can be invoked only once for a given set of parameters and the result reused without having to actually invoke the method again. The caching logic is applied transparently without any interference to the invoker.

这种方法仅适用于保证对于给定的输入(或参数)返回相同输出(结果)的方法,无论调用多少次该方法。

This approach works only for methods that are guaranteed to return the same output (result) for a given input (or arguments) no matter how many times they are invoked.

缓存抽象提供其他与缓存相关的操作,例如更新缓存内容或删除一个或所有条目。如果缓存处理在应用程序执行过程中可能发生更改的数据,这些操作很有用。

The caching abstraction provides other cache-related operations, such as the ability to update the content of the cache or to remove one or all entries. These are useful if the cache deals with data that can change during the course of the application.

与 Spring Framework 中的其他服务一样,缓存服务是一个抽象(不是缓存实现),并且需要使用实际存储来存储缓存数据。换句话说,抽象使您不必编写缓存逻辑,但并不提供实际的数据存储。此抽象通过 org.springframework.cache.Cacheorg.springframework.cache.CacheManager 接口实现。

As with other services in the Spring Framework, the caching service is an abstraction (not a cache implementation) and requires the use of actual storage to store the cache data — that is, the abstraction frees you from having to write the caching logic but does not provide the actual data store. This abstraction is materialized by the org.springframework.cache.Cache and org.springframework.cache.CacheManager interfaces.

Spring 提供了该抽象的 a few implementations:基于 JDK `java.util.concurrent.ConcurrentMap`的缓存、Gemfire 缓存、 Caffeine和兼容 JSR-107 的缓存(例如 Ehcache 3.x)。请参阅 Plugging-in Different Back-end Caches以获取有关将其他缓存存储和提供程序插入的更多信息。

Spring provides a few implementations of that abstraction: JDK java.util.concurrent.ConcurrentMap based caches, Gemfire cache, Caffeine, and JSR-107 compliant caches (such as Ehcache 3.x). See Plugging-in Different Back-end Caches for more information on plugging in other cache stores and providers.

缓存抽象对多线程和多进程环境没有特殊处理,因为此类功能由缓存实现处理。

The caching abstraction has no special handling for multi-threaded and multi-process environments, as such features are handled by the cache implementation.

如果您具有多进程环境(即部署在多个节点上的应用程序),则需要相应地配置缓存提供程序。根据您的用例,在多个节点上使用相同数据的一份副本可能就足够了。但是,如果您在应用程序执行过程中更改了数据,则可能需要启用其他传播机制。

If you have a multi-process environment (that is, an application deployed on several nodes), you need to configure your cache provider accordingly. Depending on your use cases, a copy of the same data on several nodes can be enough. However, if you change the data during the course of the application, you may need to enable other propagation mechanisms.

缓存特定项直接等效于典型的 get-if-not-found-then-proceed-and-put-eventually 代码块,该代码块在与编程缓存交互时找到。未应用锁定,且多个线程可能同时尝试加载同一项。删除也适用相同的情况。如果多个线程同时尝试更新或删除数据,您可能会使用陈旧数据。某些缓存提供程序在此领域提供高级功能。有关更多详细信息,请参阅缓存提供程序的文档。

Caching a particular item is a direct equivalent of the typical get-if-not-found-then-proceed-and-put-eventually code blocks found with programmatic cache interaction. No locks are applied, and several threads may try to load the same item concurrently. The same applies to eviction. If several threads are trying to update or evict data concurrently, you may use stale data. Certain cache providers offer advanced features in that area. See the documentation of your cache provider for more details.

要使用缓存抽象,您需要解决两个方面:

To use the cache abstraction, you need to take care of two aspects:

  • Caching declaration: Identify the methods that need to be cached and their policies.

  • Cache configuration: The backing cache where the data is stored and from which it is read.