Understanding the Cache Abstraction

Cache vs Buffer

术语“`buffer`" 和 “`cache`" 往往可以互换使用。但请注意,它们代表着不同的事物。传统上,缓冲区用作快速实体和慢速实体之间的中间临时数据存储。由于一方必须等待另一方(这会影响性能),因此缓冲区通过允许整个数据块一次移动而不是分小块移动来缓解这种情况。数据只从缓冲区写入和读出一次。此外,缓冲区至少对一个知道它的参与者是可见的。 另一方面,缓存是通过定义进行隐藏的,双方都不会意识到正在进行缓存。它也会提高性能,但它让相同的数据能够通过高速方式多次读取。 您可以在 here找到缓冲区和缓存之间差异的进一步说明。

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

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

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

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

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

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

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

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

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

  • 缓存声明:标识需要缓存的方法及其策略。

  • 缓存配置:存储和读取数据的基础缓存。