理解缓存抽象
“缓冲区
”和“缓存
”这两个术语往往可以互换使用。但是请注意,它们代表着不同的事物。传统上,缓冲区用作快速实体和慢速实体之间数据的中间临时存储。由于一方必须等待另一方(这会影响性能),缓冲区通过允许整个数据块一次性移动而不是小块移动来缓解此问题。数据只从缓冲区中写入和读取一次。此外,缓冲区对至少一个了解它的实体是可见的。
另一方面,缓存按定义是隐藏的,双方都不知道正在发生缓存。它也提高了性能,但通过允许相同的数据以快速方式多次读取来实现。
你可以在 这里找到关于缓冲区和缓存之间差异的进一步解释。
本质上,缓存抽象将缓存应用于Java方法,从而根据缓存中可用的信息减少执行次数。也就是说,每当调用目标方法时,抽象都会应用缓存行为,检查该方法是否已针对给定参数调用过。如果已调用,则返回缓存结果,而无需调用实际方法。如果该方法尚未调用,则调用它,并将结果缓存并返回给用户,以便下次调用该方法时,返回缓存结果。通过这种方式,昂贵的方法(无论是CPU密集型还是IO密集型)只需为给定的一组参数调用一次,并且可以重用结果,而无需再次实际调用该方法。缓存逻辑透明地应用,不会对调用者造成任何干扰。
这种方法仅适用于保证对于给定输入(或参数)无论调用多少次都返回相同输出(结果)的方法。
缓存抽象提供了其他与缓存相关的操作,例如更新缓存内容或删除一个或所有条目的能力。如果缓存在应用程序运行期间处理可能更改的数据,这些操作会很有用。
与Spring Framework中的其他服务一样,缓存服务是一种抽象(而不是缓存实现),并且需要使用实际存储来存储缓存数据——也就是说,抽象将你从编写缓存逻辑中解放出来,但它不提供实际的数据存储。这种抽象由 org.springframework.cache.Cache
和 org.springframework.cache.CacheManager
接口实现。
Spring 提供了 一些实现这种抽象的方式:基于JDK java.util.concurrent.ConcurrentMap
的缓存、Gemfire 缓存、Caffeine 和符合JSR-107标准的缓存(例如Ehcache 3.x)。有关插入其他缓存存储和提供程序的更多信息,请参阅 插入不同的后端缓存。
缓存抽象没有对多线程和多进程环境进行特殊处理,因为此类功能由缓存实现处理。
如果你有一个多进程环境(即应用程序部署在多个节点上),你需要相应地配置你的缓存提供程序。根据你的用例,在多个节点上复制相同的数据可能就足够了。但是,如果你在应用程序运行期间更改数据,你可能需要启用其他传播机制。
缓存特定项直接等同于在使用编程缓存交互时常见的 get-if-not-found-then-proceed-and-put-eventually 代码块。 不应用锁,多个线程可能会尝试并发加载同一项。 这同样适用于逐出。如果多个线程尝试并发更新或逐出数据,你可能会使用过时的数据。某些缓存提供程序在该领域提供高级功能。有关更多详细信息,请参阅你的缓存提供程序文档。
要使用缓存抽象,你需要关注两个方面:
-
缓存声明:识别需要缓存的方法及其策略。
-
缓存配置:存储数据并从中读取数据的后端缓存。