Java缓存备忘大全
Java 缓存是一种用于在临时存储区域(称为缓存)中存储和管理经常访问的数据的技术,以提高应用程序性能。缓存通过保持副本随时可用,有助于减少获取或计算数据所需的时间和资源。
在 Java 中,缓存可以在各个级别实现,包括:
- 内存缓存: Java 使用Guava Cache或CaffeineHashMap等数据结构或专用库提供内存缓存机制。这些允许开发人员将键值对存储在内存中,从而使数据检索比从原始来源获取更快。
- 分布式缓存:在分布式系统中,缓存可以跨多个节点扩展以共享缓存数据。Ehcache、Apache Ignite、Oracle Coherence、Infinispan、VMware GemFire或Redis 等库和平台提供分布式缓存解决方案,支持在应用程序的不同实例之间高效共享缓存数据。
- 应用程序级缓存:许多 Java 框架和库都提供内置缓存机制。例如,Spring@Cacheable提供了全面的缓存抽象,允许开发人员通过简单地使用缓存注释(如、 和)来注释方法,从而轻松地将缓存集成到他们的应用程序中@CachePut,并@CacheEvict在方法级别控制缓存行为。
- HTTP 缓存:对于 Web 应用程序,Java 支持使用Cache-Control和等标头的 HTTP 缓存机制ETag。这些标头使浏览器和 Web 服务器能够缓存图像、样式表和脚本等资源,从而减少在后续请求时重新下载它们的需要。
- 数据库缓存:可以在数据库级别应用缓存来存储频繁访问的查询结果。Java 应用程序通常利用Hibernate等技术进行对象关系映射,其中包括缓存功能。
Java 缓存在优化应用程序性能方面发挥着至关重要的作用,特别是在数据检索或计算资源密集型的场景中。必须根据应用程序的具体需求仔细设计和配置缓存策略,以在提高性能和高效资源利用之间取得平衡。
JCache API (JSR 107)
JSR-107,通常称为 JCache,是一个用于缓存 API 和标准化缓存注释的 Java 规范。JCache 的目标是为 Java 应用程序提供一种与缓存系统交互的通用方法,从而促进跨不同缓存提供程序(如上面提到的缓存提供程序)的可移植性。
JCache 的主要功能包括:
- API标准化: JCache定义了一组用于与缓存交互的标准接口和类。它不依赖于特定的缓存实现。相反,它允许集成符合规范的多个缓存提供程序。
- 注解: JCache 引入了诸如@CacheResult、@CachePut、 之类的注解,@CacheRemove可用于以声明方式控制缓存行为。这些注释与Spring等框架中的注释类似。
- 基本缓存操作: JCache 支持基本缓存操作,包括从缓存中放入、获取和删除条目。它提供了执行这些操作的标准化方法,无论底层缓存提供程序如何。
- 配置:该规范定义了配置缓存设置的标准化方法,例如缓存大小、逐出策略和过期时间。这使得管理和调整不同实现中的缓存行为变得更加容易。
- 与 Java EE 和 SE 集成: JCache 旨在与 Java EE(企业版)和 Java SE(标准版)环境无缝集成。这确保了应用程序可以在不同的 Java 平台上使用相同的缓存 API。
使用 JCache 等标准 API,开发人员可以在不同的缓存实现之间切换,并选择最适合其需求的缓存解决方案,而无需对应用程序进行最少的代码更改。此外,它还提高了生产力,因为它确保学习曲线更小,因为它仅限于 JCache 作为标准的知识,而不是每个供应商实施的具体情况。
JCache 案例
要使用 JCache 创建一个简单的“Hello World”示例,您首先需要在项目中包含 JCache API 和特定的缓存提供程序。
<dependencies> |
以下示例设置一个缓存,放入“Hello, World!” 问候语进入缓存,检索它,并将其打印到控制台。
import javax.cache.Cache; |
javax.cache.Cache接口
javax.cache.Cache接口是 Java 缓存 API 的基本部分。它代表一个缓存,是键值对的临时存储区域,可以快速存储和检索数据。它提供了存储、检索和操作缓存数据的方法。它抽象了底层缓存实现,允许开发人员通过通用接口与不同的缓存提供程序进行交互。
- V get(K key) 从缓存中检索与指定键关联的值,或者null如果未找到该键。
- void put(K key, V value) 将指定值与缓存中的指定键相关联。
- boolean containsKey(K key) 检查缓存中是否包含指定键的条目。
- V getAndPut(K key, V value) 检索与指定键关联的当前值,然后使用新值更新该值。
- void remove(K key) 从缓存中删除指定键的条目(如果存在)。
- boolean remove(K key, V oldValue) 仅当指定键当前映射到指定值时,才从缓存中删除该条目。
- void removeAll(Set<? extends K> keys) 从缓存中删除多个键的条目。
- void removeAll() 从缓存中删除所有条目。
- void putAll(Map<? extends K, ? extends V> entries) 将多个键值对与缓存关联。
- boolean putIfAbsent(K key, V value) 如果指定键尚未与值关联,则将指定值与缓存中的指定键关联。
- void clear() 清除缓存,删除所有条目。
- void close() 关闭缓存,释放与其关联的所有资源。
javax.cache.CacheManager
javax.cache.CacheManager接口负责管理缓存环境中的缓存及其配置。它提供了创建、检索和管理缓存的方法,以及访问配置和统计等特定于缓存的功能。
- CachingProvider getCachingProvider() 检索CachingProvider与 this 关联的CacheManager.
- void close() 关闭它CacheManager并释放与其关联的所有资源。
- void destroyCache(String cacheName) 销毁由其名称指定的缓存。
- boolean isClosed() 检查这是否CacheManager已关闭。
- boolean isSupported(OptionalFeature feature) 检查 this 是否支持指定的可选功能CacheManager。
- void enableManagement(String cacheName, boolean enabled) 启用或禁用对其名称指定的缓存的管理(统计和监视)。
- void enableStatistics(String cacheName, boolean enabled) 启用或禁用由名称指定的缓存的统计信息收集。
- void createCache(String cacheName, Configuration<K,V> configuration) 创建具有指定名称和配置的缓存。
- void destroyCache(String cacheName) 销毁由其名称指定的缓存。
- C getCache(String cacheName, Class<K> keyType, Class<V> valueType) 按名称检索缓存,指定键和值类型。
- Iterable<String> getCacheNames() 检索由此管理的缓存名称的可迭代集合CacheManager。
- ClassLoader getClassLoader() 检索ClassLoader由此管理的缓存所使用的内容CacheManager。
在缓存环境中管理缓存及其配置的基本功能。开发人员可以根据应用程序的需要使用这些方法来创建、访问和配置缓存。
javax.cache.spi.CachingProvider接口
javax.cache.spi.CachingProvider接口充当创建和访问缓存相关实体(例如CacheManager实例)的工厂。它充当应用程序和底层缓存实现之间的抽象层。
- CacheManager getCacheManager() CacheManager检索与 this 关联的默认值CachingProvider。
- boolean isSupported(OptionalFeature feature) 检查 this 是否支持指定的可选功能CachingProvider。
- void close() 关闭它CachingProvider并释放与其关联的所有资源。
- String getDefaultURI() 检索 this 的默认 URI CachingProvider。
- Properties getDefaultProperties() 检索用于配置此的默认属性CachingProvider。
开发人员能够管理缓存提供程序、创建缓存管理器以及在其应用程序中配置缓存行为。它们提供了处理缓存相关任务的灵活性,同时抽象了底层缓存实现细节。
javax.cache.Caching
javax.cache.Caching是一个实用程序类,用作访问 Java 应用程序中的缓存功能的入口点。它提供静态方法来创建和访问缓存提供程序和缓存管理器。它抽象了获取缓存相关实体的过程,使开发人员更容易将缓存功能集成到他们的应用程序中。
- CachingProvider getCachingProvider() 检索默认缓存提供程序。
- CacheManager getCacheManager() 检索默认缓存管理器。
- void closeCacheManager(CacheManager cacheManager) 关闭指定的缓存管理器。
- Iterable<CachingProvider> getCachingProviders() 检索所有已注册缓存提供程序的可迭代集合。
- Iterable<CacheManager> getCacheManagers(CachingProvider cachingProvider) 检索与指定缓存提供程序关联的缓存管理器的可迭代集合。
javax.cache.configuration
javax.cache.configuration包包含用于在 Java 应用程序中配置缓存和缓存管理器的类和接口。这些类和接口允许开发人员定义和自定义缓存行为的各个方面,例如事件侦听器、缓存加载器/编写器和统计信息收集。一般来说,它提供了一种根据特定应用程序需求定义缓存行为和设置的灵活方法。
- MutableConfiguration(Class<K> keyType, Class<V> valueType) MutableConfiguration使用指定的键和值类型构造一个新的。
- setStoreByValue(boolean isStoreByValue) 设置缓存是否应按值或按引用存储值。
- setTypes(Class<K> keyType, Class<V> valueType) 设置缓存的键和值类型。
- setExpiryPolicyFactory(Factory<? extends ExpiryPolicy> factory) 设置为缓存条目创建过期策略的工厂。
- setStatisticsEnabled(boolean isStatisticsEnabled) 设置是否启用缓存的统计信息收集。
- setManagementEnabled(boolean isManagementEnabled) 设置是否为缓存启用管理(监控和配置)。
- addCacheEntryListenerConfiguration(CacheEntryListenerConfiguration<K, V> listenerConfiguration) 将缓存条目侦听器配置添加到缓存。
- removeCacheEntryListenerConfiguration(CacheEntryListenerConfiguration<K, V> listenerConfiguration) 从缓存中删除缓存条目侦听器配置。
- setCacheWriterConfiguration(CacheWriterConfiguration<K, V> cacheWriterConfiguration) 设置缓存的缓存写入器配置。
- setCacheLoaderConfiguration(CacheLoaderConfiguration<K, V> cacheLoaderConfiguration) 设置缓存的缓存加载器配置。
- setReadThrough(boolean isReadThrough) 设置如果在缓存中未找到值,缓存是否应读取缓存加载器。
- setWriteThrough(boolean isWriteThrough) 设置缓存是否应写入缓存写入器。
javax.cache.configuration.MutableConfiguration提供接口的可变实现Configuration。它允许开发人员在运行时修改缓存设置,例如过期策略、大小限制、侦听器和统计信息收集。
javax.cache.expiry.ExpiryPolicy接口
javax.cache.expiry.ExpiryPolicy接口定义了缓存条目的过期策略。它指定条目在被视为过期并可能从缓存中逐出之前应在缓存中保持有效的时间。ExpiryPolicy允许开发人员根据各种标准(例如创建时间、上次访问时间或两者的组合)指定不同的过期策略。默认情况下,a 中的条目javax.cache.Cache不会过期。
- AccessedExpiryPolicy 根据上次访问时间使条目过期。当访问某个条目时,其过期时间会从上次访问开始延长固定的持续时间。
- CreatedExpiryPolicy 根据条目的创建时间使条目过期。条目在其创建时间的固定持续时间后过期。
- EternalExpiryPolicy 指示缓存的条目永不过期。条目无限期地保留在缓存中,直到显式删除为止。
- ModifiedExpiryPolicy 根据条目的修改时间使条目过期。当条目更新时,其过期时间会从上次更新后延长固定的持续时间。
监听器和监听器过滤器
在 Java 缓存 API 中,侦听器是用于监视与缓存操作相关的事件并对其做出反应的机制。这些事件包括条目创建、更新、删除和驱逐。JCache 提供两种主要类型的监听器:缓存条目监听器和缓存管理器监听器。此外,JCache 支持侦听器过滤器,允许开发人员指定触发侦听器的条件。
缓存条目侦听器
当缓存条目上发生特定事件时,例如创建、更新、删除或逐出条目时,将调用缓存条目侦听器。这些侦听器可以执行日志记录、触发通知或根据缓存事件更新外部系统等操作。
缓存管理器监听器
缓存管理器侦听器监视与缓存管理器相关的事件,例如创建或关闭缓存管理器的时间。它们提供了在缓存管理器实例化或销毁时执行初始化或清理任务的钩子。
监听器过滤器
侦听器过滤器允许开发人员指定触发侦听器的条件。例如,过滤器可用于仅在满足某些条件时调用侦听器,例如当缓存条目的值满足特定条件或使用特定键更新条目时。
import javax.cache.event.*; |
在这个例子中:
- MyCacheEntryListener类实现CacheEntryCreatedListener监听缓存条目创建事件的接口。
- MyCacheListenerFilter类实现了CacheEntryEventFilter根据缓存条目的值过滤事件的接口。
- CacheListenerExample类演示如何向过滤器注册缓存条目侦听器并执行触发侦听器的缓存操作。
下表列出了 Java 缓存 API 中一些最流行的侦听器接口类,以及它们的覆盖方法和它们处理的事件类型。
- CacheEntryCreatedListener<K, V> onCreated 缓存条目创建
- CacheEntryUpdatedListener<K, V> onUpdated 缓存条目更新
- CacheEntryRemovedListener<K, V> onRemoved 缓存条目删除
- CacheEntryExpiredListener<K, V> onExpired 缓存条目过期
- CacheEntryEvictedListener<K, V> onEvicted 缓存条目驱逐
- CacheEntryReadListener<K, V> onRead 缓存条目读取(访问)
- CacheEntryWriteListener<K, V> onWrite 缓存条目写入(放置或替换)
- CacheEntryListener<K, V> onCreated, onUpdated, onRemoved, onExpired,onEvicted 多种事件类型
- CacheManagerListener onManagerCreated,onManagerDestroyed 缓存管理器创建/销毁
Loader和Writer
缓存加载器和缓存写入器是负责在缓存缺失或更新时与外部数据源交互的组件。它们允许开发人员将缓存与底层数据存储系统集成,实现缓存数据与持久性数据源之间的无缝同步。
缓存加载器CacheLoader
缓存加载器负责在缓存中找不到请求数据(缓存缺失)时,将外部数据源的数据加载到缓存中。它们提供一种机制,用来自数据库或远程服务等持久性存储的数据填充缓存,以确保缓存中的请求数据可供后续访问使用。缓存阅读器通常与直读缓存策略结合使用,在直读缓存策略中,缓存被视为主数据存储,缺失的缓存条目会立即从集成的后端存储中获取。
- javax.cache.CacheLoader<K, V> load 将指定键的数据从外部源加载到缓存中。
- loadAll 从外部源将多个键值对加载到缓存中。
缓存写入器
另一方面,缓存编写器负责将对缓存数据所做的更改传播回外部数据源。它们通过对缓存数据所做的更改来更新持久存储,从而确保缓存与底层数据源之间的一致性。缓存写入器通常与write-through缓存策略结合使用,其中数据修改会立即反映在缓存和外部数据存储中。
- javax.cache.CacheWriter<K, V> write 将指定键值对的数据从缓存写入外部数据源。
- delete 从外部数据源删除指定键的数据。
- writeAll 将多个键值对从缓存写入外部数据源。
- deleteAll 从外部数据源删除多个键及其关联值。
缓存条目处理器EntryProcessor
由接口表示的高速缓存条目处理器javax.cache.EntryProcessor是一种对高速缓存条目执行原子操作的机制。它允许开发人员直接在缓存节点(JVM)内执行自定义逻辑,提供了一种操作缓存条目的方法,而无需外部数据源、客户端和缓存节点之间的数据序列化/反序列化或复杂的同步机制。
缓存条目处理器通常用于需要原子执行多个缓存操作的场景,以确保一致性并避免竞争条件。当缓存分布在多个节点上(这种情况很常见)时,它们也特别有用。它们提供了一种在单个原子操作中封装和执行缓存条目上的自定义逻辑的方法,从而提高性能并降低数据不一致的风险。
下面介绍javax.cache.EntryProcessor基本方法。
- process 以原子方式对缓存条目执行自定义逻辑,确保缓存条目在处理过程中被锁定。
- processAll 以原子方式对多个缓存条目执行自定义逻辑,确保每个缓存条目在处理过程中都被锁定。
javax.cache.annotation
javax.cache.annotation包提供了注释,开发人员可以使用注释来标记用于缓存目的的方法。这些注释提供了一种控制缓存行为的便捷方法,例如指定缓存名称、缓存条目键和缓存策略,而无需在方法实现中显式缓存逻辑。
- @CacheResult 标记一个方法,其返回值应该被缓存。指定缓存名称和键,以及可选的缓存解析程序。也适用于类,以影响该类的所有方法。
- @CachePut 标记其返回值应在缓存中缓存或更新的方法。指定缓存名称和键。也适用于类,以影响该类的所有方法。
- @CacheKey 显式指定方法参数作为缓存键。
- @CacheValue 使用时显式指定一个方法参数作为缓存值@CachePut
- @CacheRemove 标记从缓存中删除条目的方法。指定缓存名称和键。也适用于类,以影响该类的所有方法。
- @CacheRemoveAll 标记一个从缓存中删除所有条目的方法。指定缓存名称。也适用于类,以影响该类的所有方法。
- @CacheDefaults 指定类中方法的默认缓存设置。可以定义默认缓存名称、键生成器等。仅适用于类。
import javax.cache.annotation.*; |
监控与管理
Java Caching API 在运行时提供管理和监视选项,以方便观察和控制缓存行为。这些选项使开发人员能够监控缓存使用情况、性能指标和配置详细信息,并动态管理缓存生命周期和操作。
管理和监控选项
- JMX(Java 管理扩展):JCache 支持与 JMX 集成,允许缓存实现将缓存管理和监控功能公开为托管 bean。通过 JMX,开发人员可以通过编程方式或通过 JConsole 或 VisualVM 等管理工具访问缓存相关的属性和操作。
- 指标和统计信息:JCache 实现通常为收集和公开缓存使用指标和统计信息提供内置支持。这些指标可能包括命中/未命中率、缓存大小、逐出计数和延迟测量,从而提供对缓存性能和有效性的深入了解。
用于 JCache 管理的 JMX API
下面的表格列出了一些最常用的用于管理和监控 JCache 实现的 JMX API:
- javax.cache.management.CacheMXBean 通过 JMX 公开缓存管理和监控功能,例如缓存统计信息、配置详细信息和操作。
- javax.cache.management.CacheStatisticsMXBean 通过 JMX 提供对缓存统计信息的访问,例如命中/未命中计数、逐出计数和缓存大小。
- javax.cache.management.CacheManagerMXBean 表示缓存管理器的管理接口,通过 JMX 公开缓存创建、销毁和管理的方法。
- javax.cache.management.CacheManagerStatisticsMXBean 提供缓存管理器统计信息,例如通过 JMX 创建、销毁和剩余的缓存数量。
- javax.cache.management.CacheMXBean.getCacheMXBeans() 检索与缓存管理器关联的缓存 MXBean 集合。
这些 JMX API 提供用于访问缓存和缓存管理器管理和监控功能的标准化接口。通过与 JMX 集成,JCache 实现提供了一致且可互操作的方法来管理和监视运行时的缓存操作。
供应商特定功能
它的unwrap方法允许开发人员获取与特定 JCache 类或接口关联的底层特定于实现的对象。当开发人员需要访问标准 JCache API 未提供的特定于实现的特性或功能时,此方法非常有用。
- javax.cache.Cache 表示 JCache API 中的缓存。允许存储、检索和管理缓存的键值对。
- javax.cache.CacheManager 代表 JCache API 中的缓存管理器。管理缓存的生命周期并提供缓存创建。
- javax.cache.Cache.Entry 代表缓存中的一个条目。提供对与条目关联的键、值和元数据的访问。
// Unwrap Cache to Hazelcast ICache |
请记住,如果您需要真正的缓存提供程序可移植性,则不建议使用此功能,因为您的应用程序将耦合到特定于供应商的 API。
缓存拓扑和模式
在缓存系统的上下文中,缓存拓扑是指分布式缓存环境中缓存的排列或结构。不同的缓存拓扑在性能、可扩展性和一致性方面提供了不同的权衡。以下是一些常见的缓存拓扑:
- 单节点缓存:在此拓扑中,只有一个缓存节点,通常运行在一台服务器或实例上。它是最简单的缓存形式,并提供基本的缓存功能。然而,它缺乏可扩展性和容错能力。
- 多个独立缓存:多个缓存节点独立运行,每个节点管理自己的缓存。与单节点缓存相比,这种拓扑具有更好的可扩展性,因为多个缓存实例可以同时处理请求。然而,它缺乏跨缓存的数据一致性。
- 复制缓存:在复制缓存拓扑中,整个数据集在所有缓存节点上复制。这确保每个缓存节点都保存数据的完整副本。它提供高可用性和容错能力,因为任何节点故障都可以通过其他副本来缓解。但是,由于数据重复,可能会导致网络流量和内存消耗增加。
- 分区缓存:在分区缓存拓扑中,数据集基于一致的哈希算法跨多个缓存节点进行分区。每个节点负责存储和管理数据的子集。这允许水平可扩展性,因为数据集可以增长超出单个节点的容量。然而,管理跨分区的数据一致性和缓存一致性可能具有挑战性。
与缓存拓扑类似,缓存模式是指缓存是客户端应用程序的一部分还是作为单独的服务运行。一般来说,以下模式在缓存中很常见:
- 客户端缓存(嵌入式模式):在此模式下,缓存在客户端执行,通常在应用程序的 JVM 内。缓存数据存储在本地,减少了从远程缓存或服务器获取数据的需要。它可以通过减少网络延迟和服务器负载来提高应用程序性能。但如果缓存与服务器端数据不同步,可能会导致数据不一致。
- 服务器端缓存(客户端-服务器模式):与客户端缓存相反,服务器端缓存将缓存数据存储在缓存服务器或节点内。它减轻了客户端应用程序的缓存责任,并集中了缓存管理和协调。服务器端缓存可以更好地控制缓存策略和数据一致性,但可能会为缓存检索操作引入额外的网络延迟。
每种缓存拓扑和模式都有其优点和缺点,拓扑/模式组合的选择取决于应用程序需求、可扩展性需求、容错和数据一致性考虑等因素。组织通常采用缓存拓扑和模式的组合来有效地满足其特定的缓存要求。
资源
以下资源提供了有关缓存系统的丰富信息,从基本概念到高级主题,为使用缓存技术的开发人员、架构师和系统管理员提供了宝贵的参考。
- Oracle Java 缓存 API 文档:
- 网站:Oracle 文档
- 描述:官方文档提供了有关 Java 缓存 API (JCache) 的全面信息,包括指南、教程和 API 参考。
- 网站:Ehcache 文档
- 描述:Ehcache 的文档提供了有关配置、部署和管理基于 Ehcache 的缓存解决方案的深入资源,以及最佳实践和故障排除指南。
- 网站:Apache Ignite 文档
- 描述:Apache Ignite 的文档提供了有关使用 Ignite 进行分布式缓存、内存计算和数据处理的详细指南和教程。
- 网站:Spring 缓存文档
- 描述:Spring Framework 的缓存文档提供了有关使用 Spring 的缓存抽象以及与各种缓存提供程序集成的指南,包括设置、配置和使用示例。