Springboot - 15.二级分布式缓存集成-Caffeine
👀中文文档
- Caffeine
👀使用Caffeine (本地缓存)
当与Spring Boot结合使用时,Caffeine提供了一个直观且功能强大的二级缓存解决方案。Spring Boot的缓存抽象使得整合Caffeine变得相当简单。以下是如何在Spring Boot应用中使用Caffeine作为二级缓存的方法:
结合Spring Boot,让我们对Caffeine的各个方面进行一个快速概览:
✌1. 添加:
- 手动或自动地将条目添加到缓存中。
- 使用
cache.put(key, value)
可以手动添加。
✍1. 手动加载:
-
作用: 手动加载是在需要时显式地调用缓存的
get
方法来加载数据。 -
使用场景: 适用于需要精确控制加载时机和逻辑的情况。
-
示例代码(基于 Spring Boot):
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;@Service
public class MyService {private final Cache myCache;public MyService(CacheManager cacheManager) {this.myCache = cacheManager.getCache("myCache");}public String getDataById(String id) {Cache.ValueWrapper valueWrapper = myCache.get(id);if (valueWrapper != null) {return (String) valueWrapper.get();} else {// 手动加载数据的逻辑String loadedData = loadData(id);myCache.put(id, loadedData);return loadedData;}}private String loadData(String id) {// 手动加载数据的逻辑// ...return data;}
}
✍2. 自动加载:
-
作用: 自动加载是指当缓存中不存在某个键的数据时,自动触发加载数据并将其存入缓存。
-
使用场景: 适用于数据加载逻辑相对简单的情况。
-
示例代码(基于 Spring Boot):
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class MyService {@Cacheable(value = "myCache", key = "#id")public String getDataById(String id) {// 自动加载数据的逻辑String loadedData = loadData(id);return loadedData;}private String loadData(String id) {// 数据加载逻辑// ...return data;}
}
✍3. 手动异步加载:
-
作用: 手动异步加载是在需要数据时,手动调用异步方法来加载数据,然后将加载的数据存入缓存。
-
使用场景: 适用于数据加载可能耗时较长且不希望阻塞主线程的情况。
-
示例代码(基于 Spring Boot):
import org.springframework.scheduling.annotation.Async;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;@Service
public class MyService {private final Cache myCache;public MyService(CacheManager cacheManager) {this.myCache = cacheManager.getCache("myCache");}@Asyncpublic void fetchDataAsync(String id) {String loadedData = fetchDataFromDatabase(id);if (loadedData != null) {myCache.put(id, loadedData);} else {// 如果数据为空,可以进行一些处理,例如记录日志等}}private String fetchDataFromDatabase(String id) {// 异步加载数据的逻辑,例如从数据库获取数据// ...return data;}
}
✍4. 自动异步加载:
-
作用: 自动异步加载是指当缓存中不存在某个键的数据时,自动触发异步加载数据并将其存入缓存。
-
使用场景: 适用于数据加载可能耗时较长且需要自动触发的情况。
-
示例代码(基于 Spring Boot):
import org.springframework.scheduling.annotation.Async;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class MyService {@Async@Cacheable(value = "myCache", key = "#id")public String getDataById(String id) {// 异步加载数据的逻辑String loadedData = loadData(id);return loadedData;}private String loadData(String id) {// 异步加载数据的逻辑// ...return data;}
}
✌2. 驱逐:
- 基于大小、权重或时间等策略来移除缓存条目。
maximumSize
、expireAfterAccess
等策略可用于配置。- 在 Spring Boot 中结合 Caffeine 缓存库,Caffeine 提供了三种驱逐(eviction)策略,用于控制缓存中的数据何时被清除。这三种驱逐策略分别是基于大小的驱逐、基于时间的驱逐和基于引用的驱逐。下面将为每种策略提供作用、使用场景、优缺点和示例代码,并结合 Spring Boot 进行说明。
✍1. 基于大小的驱逐:
-
作用: 基于大小的驱逐策略在缓存达到一定大小限制时,会驱逐一些缓存项以腾出空间。
-
使用场景: 适用于需要限制缓存大小,以防止内存占用过多的情况。
-
优点: 可以有效控制缓存的内存使用,避免内存溢出问题。
-
缺点: 不适用于某些情况,如需要保留所有数据而不基于大小驱逐。
代码配置示例:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().maximumSize(100) // 最大缓存项数量.build();}@Beanpublic CaffeineCacheManager cacheManager() {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(caffeineConfig());return caffeineCacheManager;}
}
配置文件配置示例:
# application.properties
spring.cache.cache-names=myCache
spring.cache.caffeine.spec=maximumSize=100
✍2. 基于时间的驱逐:
-
作用: 基于时间的驱逐策略会根据设置的过期时间自动清除缓存项。
-
使用场景: 适用于需要缓存数据有时效性的场景,例如缓存一些临时数据。
-
优点: 简单易用,适用于具有时效性数据的场景。
-
缺点: 不适用于那些需要在数据使用频繁时保持缓存的场景。
代码配置示例:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES) // 设置过期时间.build();}@Beanpublic CaffeineCacheManager cacheManager() {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(caffeineConfig());return caffeineCacheManager;}
}
配置文件配置示例:
# application.properties
spring.cache.cache-names=myCache
spring.cache.caffeine.spec=expireAfterWrite=30m
✍3. 基于引用的驱逐:
-
作用: 基于引用的驱逐策略根据 Java 的引用类型决定是否清除缓存项。
-
使用场景: 适用于需要更加灵活的缓存策略,可以根据引用情况进行驱逐。
-
优点: 可以更精细地控制缓存项的驱逐,适应不同类型的需求。
-
缺点: 较复杂,需要了解 Java 的引用类型。
代码配置示例:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().weakKeys() // 使用弱引用作为键.weakValues() // 使用弱引用作为值.build();}@Beanpublic CaffeineCacheManager cacheManager() {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(caffeineConfig());return caffeineCacheManager;}
}
配置文件配置示例:
# application.properties
spring.cache.cache-names=myCache
spring.cache.caffeine.spec=weakKeys,weakValues
✌3. 移除:
- 当条目不再需要时,它们会被移除。
- 使用
cache.invalidate(key)
可以手动移除条目。
当结合 Spring Boot 使用 Caffeine 缓存库时,下面是 Caffeine 提供的三种移除(eviction)策略,以及针对每种策略的作用、使用场景、优缺点和示例代码,分别提供了通过代码配置和配置文件配置的示例。
✍1. SIZE_BASED:
-
作用: 在缓存项数量达到指定阈值时,移除最近最少使用的缓存项。
-
使用场景: 适用于需要限制缓存大小,同时保留经常访问的数据的场景。
-
优点: 能够限制缓存项数量,同时保持常用数据的缓存。
-
缺点: 可能会移除一些仍然有用的数据。
代码配置示例:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().maximumSize(100) // 最大缓存项数量.evictionListener((key, value, cause) ->System.out.println("Evicted key: " + key + ", cause: " + cause)).build();}@Beanpublic CaffeineCacheManager cacheManager() {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(caffeineConfig());return caffeineCacheManager;}
}
配置文件配置示例:
# application.properties
spring.cache.cache-names=myCache
spring.cache.caffeine.spec=maximumSize=100
✍2. TIME_BASED:
-
作用: 在缓存项的固定时间段内不被访问时,移除缓存项。
-
使用场景: 适用于需要保留数据一段时间,并且数据在一段时间后变得过期或不再有效的场景。
-
优点: 简单易用,适用于有时效性需求的数据。
-
缺点: 无法根据数据的实际使用情况来移除。
代码配置示例:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.concurrent.TimeUnit;
import org.springframework.cache.caffeine.CaffeineCacheManager;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().expireAfterAccess(30, TimeUnit.MINUTES) // 访问后过期时间.evictionListener((key, value, cause) ->System.out.println("Evicted key: " + key + ", cause: " + cause)).build();}@Beanpublic CaffeineCacheManager cacheManager() {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(caffeineConfig());return caffeineCacheManager;}
}
配置文件配置示例:
# application.properties
spring.cache.cache-names=myCache
spring.cache.caffeine.spec=expireAfterAccess=30m
✍3. ACCESS_BASED:
-
作用: 在缓存项被访问一定次数后,移除缓存项。
-
使用场景: 适用于需要根据访问频率移除缓存的数据。
-
优点: 可以根据实际使用情况移除缓存项,保留经常访问的数据。
-
缺点: 需要实时监控缓存项的访问次数。
代码配置示例:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().maximumSize(100) // 最大缓存项数量.recordStats() // 启用统计.evictionListener((key, value, cause) ->System.out.println("Evicted key: " + key + ", cause: " + cause)).build();}@Beanpublic CaffeineCacheManager cacheManager() {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(caffeineConfig());return caffeineCacheManager;}
}
配置文件配置示例:
# application.properties
spring.cache.cache-names=myCache
spring.cache.caffeine.spec=maximumSize=100,recordStats
✍4. 驱逐移除示例:
-
作用: 驱逐是缓存中数据移除的一种策略,通过特定的条件和算法,从缓存中移除一些数据以腾出空间。
-
引起原因: 驱逐通常由缓存达到容量限制或者基于时间、大小、引用等的设定的条件触发。最常见的例子是基于大小的驱逐,当缓存项数量超过了一定的最大值时,系统会自动移除一些缓存项。
-
示例: 在基于大小的驱逐策略中,当缓存项数量超过一定限制时,会移除最不常用的缓存项。这是通过 Caffeine 的
maximumSize
设置来实现的。
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().maximumSize(100) // 最大缓存项数量.build();}@Beanpublic CaffeineCacheManager cacheManager() {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(caffeineConfig());return caffeineCacheManager;}
}
✍5. 失效示例:
-
作用: 失效是手动移除缓存元素的操作,通常由应用程序代码触发。
-
引起原因: 失效通常由开发人员在特定情况下主动调用,比如当数据被更新、删除或者某些条件满足时,开发人员可以选择手动将相应的缓存项标记为无效。
-
示例: 失效是手动从缓存中移除特定数据的操作。在 Spring Boot 中,你可以通过调用缓存管理器的
evict
方法来手动使缓存项失效。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;@Configuration
@EnableCaching
public class CacheConfig {@Autowiredprivate CacheManager cacheManager;// 每小时触发一次失效操作@Scheduled(fixedRate = 3600000)public void invalidateCache() {cacheManager.getCache("myCache").clear();}
}
✍6. 移除示例:
-
作用: 移除是驱逐和失效的结果,即从缓存中删除数据。
-
引起原因: 移除可以是基于驱逐策略(比如基于大小、时间等)的结果,也可以是开发人员手动调用失效操作导致的结果。
-
示例: 移除是驱逐和失效的结果,导致缓存中的数据被删除。下面是基于失效和驱逐的移除示例:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().maximumSize(100) // 最大缓存项数量.evictionListener((key, value, cause) ->System.out.println("Evicted key: " + key + ", cause: " + cause)).build();}@Beanpublic CaffeineCacheManager cacheManager() {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(caffeineConfig());return caffeineCacheManager;}
}
在 Spring Boot 中使用 Caffeine 缓存库,显式移除(Explicit Removal)和移除监听器(Removal Listener)是两个重要的概念,用于更精细地控制缓存数据的移除行为。下面将详细解释这两个概念,并提供项目性的示例代码。
✍7. 显式移除:
-
作用: 显式移除是通过代码手动从缓存中移除特定的缓存项。
-
使用场景: 适用于需要根据特定条件或操作主动清除缓存项的场景,比如数据更新、删除等。
-
优点: 可以根据业务需求灵活地选择要移除的缓存项,从而避免缓存中存储无效或过时数据。
-
缺点: 需要在适当的时机手动调用移除操作。
示例代码(基于 Spring Boot):
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.CacheManager;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().maximumSize(100) // 最大缓存项数量.build();}@Beanpublic CaffeineCacheManager cacheManager() {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(caffeineConfig());return caffeineCacheManager;}// 显式移除示例@CacheEvict(cacheNames = "myCache", key = "#key")public void removeCacheItem(String key) {// 此方法将从缓存中移除指定 key 的缓存项}
}
在上述示例代码中,我们使用了 Spring 的 @CacheEvict
注解来实现显式移除。在 removeCacheItem
方法中,通过指定的 key
参数调用该方法,可以手动移除缓存中的指定缓存项。
✍8. 移除监听器:
-
作用: 移除监听器是一种机制,当缓存项被移除时,会触发监听器执行特定的操作。
-
使用场景: 适用于需要在缓存项移除时执行一些后续操作的场景,比如记录日志、数据清理等。
-
优点: 允许在缓存项被移除时执行定制化的操作,增强了缓存的灵活性。
-
缺点: 需要编写额外的监听器代码,增加了一定的复杂性。
示例代码(基于 Spring Boot):
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.RemovalListener;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import java.util.logging.Logger;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().maximumSize(100) // 最大缓存项数量.removalListener(new CustomRemovalListener()) // 添加移除监听器.build();}@Beanpublic CaffeineCacheManager cacheManager() {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(caffeineConfig());return caffeineCacheManager;}// 自定义移除监听器private class CustomRemovalListener implements RemovalListener<Object, Object> {private final Logger logger = Logger.getLogger(getClass().getName());@Overridepublic void onRemoval(Object key, Object value, RemovalCause cause) {logger.info("Removed key: " + key + ", cause: " + cause);// 在缓存项被移除时记录日志}}
}
在上述示例代码中,我们通过 removalListener
方法添加了一个自定义的移除监听器,当缓存项被移除时,会调用监听器中的 onRemoval
方法,我们在这个方法中记录了移除的缓存项信息。
这些示例代码演示了如何在 Spring Boot 中结合 Caffeine 缓存库使用显式移除和移除监听器的概念,并展示了如何在项目中应用这些概念以满足特定的业务需求。
✌4. 刷新:
-
作用: 刷新策略允许在缓存中的数据保持一定时间后自动更新,确保缓存数据的新鲜性。
-
使用场景: 适用于需要定期更新缓存中数据,以保持数据的实时性的场景,比如缓存数据库查询结果。
-
优点: 使缓存数据保持较新的状态,避免数据过时。
-
缺点: 需要考虑缓存的更新频率,不适用于所有类型的数据。
✍1. 配置代码中的刷新策略示例:
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import java.util.concurrent.TimeUnit;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES) // 过期时间.refreshAfterWrite(5, TimeUnit.MINUTES) // 刷新时间.build();}@Beanpublic CaffeineCacheManager cacheManager() {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(caffeineConfig());return caffeineCacheManager;}
}
✍2. 配置文件中的刷新策略示例(application.properties):
# application.properties
spring.cache.cache-names=userCache
spring.cache.caffeine.spec=expireAfterWrite=30m,refreshAfterWrite=5m
在上述代码示例中,我们展示了如何通过代码配置和配置文件配置 Caffeine 的刷新策略。在代码配置中,我们使用了 expireAfterWrite
方法来设置缓存项的过期时间为 30 分钟,以及 refreshAfterWrite
方法来设置刷新时间为 5 分钟。在配置文件配置中,我们直接使用了 spring.cache.caffeine.spec
属性来配置刷新策略。
✍3. 示例场景:
假设你的 Spring Boot 项目中有一个用户信息缓存,你希望用户信息在一段时间后自动刷新以保持实时性。以下是一个项目性的示例:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Map;@Service
public class UserService {private Map<Long, User> userDatabase = new HashMap<>();@Cacheable(cacheNames = "userCache", key = "#userId")public User getUserById(Long userId) {System.out.println("Fetching user from database for userId: " + userId);// Simulating fetching user data from databasereturn userDatabase.get(userId);}
}
在这个示例中,getUserById
方法使用了 Spring Cache 的 @Cacheable
注解,并设置了缓存名称为 “userCache”。刷新策略的配置可以在代码中进行,也可以在配置文件中通过属性进行配置。
✌5. 计算:
- Caffeine可以使用计算的方式来加载或重新加载条目。
- 适用于需要延迟加载的数据。
当结合 Spring Boot 使用 Caffeine 缓存库时,可以利用 Caffeine 提供的不同计算策略来优化缓存的使用。以下将按照你提供的解析方向,对每个计算策略进行说明,包括作用、使用场景、优缺点以及项目中使用 Spring Boot 的示例代码。
✍1. 写模式
🎷1. 批量和合并操作
批量和合并操作是 Caffeine 缓存提供的一种计算策略,旨在优化并发访问下的缓存数据计算和填充过程。这种策略可以有效地将多个相似的请求合并为一个,从而降低计算成本、减少缓存填充次数,并提高系统性能。以下是对批量和合并操作策略的详细解释:
作用: 批量和合并操作策略的主要作用是在并发请求中将多个相似的计算请求合并成一个,以减少计算成本和缓存填充的开销。
使用场景: 批量和合并操作适用于以下场景:
- 并发访问:当多个并发请求需要计算相同结果时,可以将这些请求合并为一个,减少重复计算。
- 缓存填充:合并操作可以减少频繁的缓存填充操作,降低缓存填充的开销。
优缺点:
- 优点:
- 减少计算成本:合并相似请求可以避免重复计算,提高计算效率。
- 减少缓存填充:合并操作可以降低缓存填充次数,减少对底层数据源的访问。
- 提高性能:通过减少计算和填充开销,可以提高系统的响应性能和吞吐量。
- 缺点:
- 不适用于不相似请求:只有在请求之间存在相似性时,才能充分发挥合并操作的优势。
示例代码:
假设有一个需要进行重复计算的服务,我们可以使用 Caffeine 缓存的批量和合并操作策略来优化。
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class BatchAndMergeService {private final Cache<String, String> cache = Caffeine.newBuilder().build();@Cacheable(cacheNames = "batchAndMergeCache", key = "#key", sync = true, cacheManager = "caffeineCacheManager")public String computeValue(String key) {// Simulate a time-consuming computationtry {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return "ComputedValue for " + key;}
}
在这个示例中,computeValue
方法模拟了一个耗时的计算过程。通过使用 @Cacheable
注解,并将 sync
参数设置为 true
,我们告诉 Caffeine 缓存要应用批量和合并操作策略。当多个并发请求同时调用 computeValue
方法时,只有一个计算实际被执行,其他请求会等待该计算完成后,直接从缓存中获取计算结果,从而避免了重复计算的开销。
总之,批量和合并操作策略在并发访问情况下非常有用,可以通过合并相似请求来减少计算和缓存填充的开销,从而提高系统的性能和效率。
🎷2. 延迟操作指定的时间窗口
延迟操作指定的时间窗口是 Caffeine 缓存提供的一种计算策略,它允许你推迟对缓存值的计算,以便在实际需要使用这些值之前进行计算,从而在不影响数据有效性的情况下降低计算成本。以下是对延迟操作指定的时间窗口策略的详细解释:
作用: 延迟操作策略的主要作用是将计算操作推迟到数据被实际访问之前,以减少不必要的计算开销。
使用场景: 延迟操作指定的时间窗口适用于以下场景:
- 高成本计算:当计算某个值的成本较高时,可以推迟计算,只在需要时才进行。
- 数据保持有效性:当数据在一定时间内保持有效时,可以延迟计算以降低计算压力。
优缺点:
- 优点:
- 减少计算成本:推迟计算可以避免不必要的计算开销,提高计算效率。
- 利用数据有效性:延迟操作可以利用数据的有效性,避免计算过早导致的数据失效。
- 缺点:
- 数据可能不是最新的:由于延迟计算,缓存中的数据可能不是实时的,适用于数据有效时间较长的场景。
示例代码:
假设有一个需要复杂计算的服务,在某个时间窗口内计算结果保持有效。
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class DelayedComputeService {private final Cache<String, String> cache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES) // Data is valid for 10 minutes.build();@Cacheable(cacheNames = "delayedComputeCache", key = "#key", cacheManager = "caffeineCacheManager")public String getDelayedValue(String key) {// Simulate a time-consuming computationtry {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}return "ComputedValue for " + key;}
}
在这个示例中,getDelayedValue
方法模拟了一个耗时的计算过程。通过使用 @Cacheable
注解并配置 expireAfterWrite
,我们告诉 Caffeine 缓存将缓存值保持有效时间为 10 分钟。因此,当多个请求访问相同的 key 时,在时间窗口内,只有第一个请求会执行实际计算,其他请求直接从缓存中获取计算结果,从而减少了重复计算。
综上所述,延迟操作指定的时间窗口策略适用于需要复杂计算且数据保持有效性的场景,通过推迟计算以减少不必要的计算开销,并利用数据有效性来提高计算效率。
🎷3. 批处理数据超过阈值大小
批处理数据超过阈值大小是 Caffeine 缓存提供的一种计算策略,它允许在定期刷新之前,如果批处理数据量超过设定的阈值大小,提前触发批处理操作,确保数据及时刷新。这个策略可以有效地减轻定期刷新时的压力,确保缓存中的数据保持最新。以下是对批处理数据超过阈值大小策略的详细解释:
作用: 批处理数据超过阈值大小策略的主要作用是在定期刷新之前,根据批处理数据量是否超过阈值来提前触发批处理操作,确保缓存中的数据及时刷新。
使用场景: 批处理数据超过阈值大小适用于以下场景:
- 定期刷新:当缓存需要定期刷新时,通过批处理数据来减轻刷新时的压力。
- 数据更新频繁:当数据更新频繁,可能在定期刷新前达到阈值,可以提前触发刷新操作。
优缺点:
- 优点:
- 压力分散:通过提前触发批处理操作,可以将刷新压力分散到多个时间点,降低系统负载。
- 数据保持最新:确保缓存中的数据保持最新,避免过时数据的访问。
- 缺点:
- 阈值设置:需要根据实际情况合理设置阈值,避免过于频繁或过于稀疏的刷新。
示例代码:
假设有一个需要定期刷新的服务,我们可以使用 Caffeine 缓存的批处理数据超过阈值大小策略来优化。
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;@Service
public class BatchRefreshService {private final Cache<String, String> cache = Caffeine.newBuilder().maximumSize(100) // Set the maximum cache size.build();@CacheEvict(cacheNames = "batchRefreshCache", allEntries = true, cacheManager = "caffeineCacheManager")public void refreshCache() {// Your batch refresh logic here}
}
在这个示例中,我们使用了 @CacheEvict
注解来触发缓存的刷新操作。通过设置 maximumSize
,我们限制了缓存的最大大小,如果批处理数据量超过阈值,将触发 refreshCache
方法来执行批处理操作。
总之,批处理数据超过阈值大小策略适用于需要定期刷新的场景,通过提前触发批处理操作来分散刷新压力,确保缓存中的数据保持最新。
🎷4. 从 write-behind 缓存加载数据
从 write-behind 缓存加载数据是 Caffeine 缓存提供的一种计算策略,它允许在外部资源操作还没有刷新时,从 write-behind 缓存中加载数据,以提供最新数据。这个策略可以确保缓存中的数据实时性,即使外部资源操作尚未完成。以下是对从 write-behind 缓存加载数据策略的详细解释:
作用: 从 write-behind 缓存加载数据策略的主要作用是在外部资源操作尚未刷新时,从 write-behind 缓存中加载数据,以确保缓存数据的实时性。
使用场景: 从 write-behind 缓存加载数据适用于以下场景:
- 外部资源操作:当缓存需要加载来自外部资源的数据时,而外部资源操作还未完成时。
- 数据实时性:当要求缓存中的数据实时性,即使数据尚未持久化到外部资源。
优缺点:
- 优点:
- 数据实时性:确保缓存中的数据实时更新,无论外部资源操作是否完成。
- 最新数据:提供最新的数据给请求方,增强数据访问的准确性。
- 缺点:
- 内存开销:write-behind 缓存需要维护额外的内存空间,存储尚未持久化的数据。
示例代码:
假设有一个需要从外部资源加载数据的服务,我们可以使用 Caffeine 缓存的从 write-behind 缓存加载数据策略来确保数据实时性。
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class WriteBehindLoadService {private final Cache<String, String> cache = Caffeine.newBuilder().writer((key, value) -> {// Write-behind cache logic// This method is called when the cache entry is created or updated}).build();@Cacheable(cacheNames = "writeBehindCache", key = "#key", cacheManager = "caffeineCacheManager")public String getValue(String key) {// Your logic to load data from external resourcereturn "LoadedValue";}
}
在这个示例中,我们使用了 Caffeine.newBuilder().writer(...)
来配置 write-behind 缓存逻辑。当缓存的条目被创建或更新时,writer
方法会被调用,你可以在其中实现将缓存数据写入外部资源的逻辑。通过使用 @Cacheable
注解来触发缓存加载操作,即使外部资源操作尚未完成,也可以从 write-behind 缓存中获取数据。
综上所述,从 write-behind 缓存加载数据策略适用于需要缓存中数据实时性的场景,通过确保数据的最新性,增强了数据访问的准确性和可靠性。
🎷5. 根据外部资源特性控制重试、速率和并发度
根据外部资源特性控制重试、速率和并发度是 Caffeine 缓存提供的一种计算策略,它允许你根据外部资源的特性来控制缓存操作的重试次数、访问速率和并发度,以避免对外部资源造成过度压力。这个策略可以帮助你优化缓存的使用,确保与外部资源的交互更加稳定和可控。以下是对根据外部资源特性控制重试、速率和并发度策略的详细解释:
作用: 根据外部资源特性控制重试、速率和并发度策略的主要作用是根据外部资源的特性来优化缓存操作,确保与外部资源的交互更加稳定和可控。
使用场景: 根据外部资源特性控制重试、速率和并发度适用于以下场景:
- 远程资源调用:当缓存需要从远程资源中加载数据时,需要考虑远程资源的可靠性和稳定性。
- 避免过度请求:当缓存操作可能对外部资源造成过度压力时,需要限制重试、速率和并发度。
优缺点:
- 优点:
- 外部资源保护:通过限制重试、速率和并发度,可以保护外部资源免受过度请求的影响。
- 稳定性增强:通过优化缓存与外部资源的交互,可以增强系统的稳定性。
- 缺点:
- 复杂性增加:需要根据外部资源的特性进行合理的设置,增加了配置和调优的工作。
示例代码:
假设有一个需要与远程资源交互的服务,为了避免对远程资源的过度请求,我们可以使用 Caffeine 缓存的根据外部资源特性控制重试、速率和并发度策略来进行限制。
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class ExternalResourceService {private final Cache<String, String> cache = Caffeine.newBuilder().maximumSize(100).build();@Cacheable(cacheNames = "externalResourceCache", key = "#key", cacheManager = "caffeineCacheManager")public String getExternalData(String key) {// Your logic to interact with the external resourcereturn "ExternalData for " + key;}
}
在这个示例中,我们使用了 @Cacheable
注解来触发缓存加载操作。通过设置 maximumSize
,我们限制了缓存的最大大小,同时也可以在 Caffeine.newBuilder()
中进行其他配置,如最大并发度、重试次数等。
综上所述,根据外部资源特性控制重试、速率和并发度策略适用于需要与外部资源交互的场景,通过合理设置缓存操作的限制,保护外部资源免受过度请求的影响,提高系统的稳定性和可靠性。
✍2. 分层
分层是 Caffeine 缓存提供的一种计算策略,允许你在缓存中实现多层级的数据存储结构,通常包括两个缓存层,例如 L1 缓存和 L2 缓存。这种策略旨在通过将数据存储在不同层级的缓存中,以提高数据访问效率和灵活性。以下是对分层策略的详细解释:
作用: 分层策略的主要作用是在缓存中实现多层级的数据存储结构,以提高数据访问效率和灵活性。
使用场景: 分层适用于以下场景:
- 数据访问优化:将热门或常用数据存储在 L1 缓存中,将不常用数据存储在 L2 缓存中,以提高数据的访问效率。
- 容量控制:通过限制 L1 缓存的容量,将部分数据迁移到 L2 缓存中,以避免 L1 缓存溢出。
优缺点:
- 优点:
- 数据访问优化:将常用数据存储在 L1 缓存中,可以提高数据的访问速度和响应性能。
- 容量管理:通过分层,可以更好地控制缓存的容量,避免溢出问题。
- 缺点:
- 复杂性增加:分层策略需要额外的配置和管理,增加了系统的复杂性。
- 数据一致性:需要考虑数据在不同层级之间的一致性维护。
示例代码:
假设有一个需要数据分层存储的服务,我们可以使用 Caffeine 缓存的分层策略来实现。
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.stereotype.Service;import java.util.Arrays;@Service
public class LayeredCacheService {private final Cache<String, String> l1Cache = Caffeine.newBuilder().maximumSize(100).build();private final Cache<String, String> l2Cache = Caffeine.newBuilder().maximumSize(1000).build();public LayeredCacheService() {SimpleCacheManager cacheManager = new SimpleCacheManager();cacheManager.setCaches(Arrays.asList(new CaffeineCache("l1Cache", l1Cache), new CaffeineCache("l2Cache", l2Cache)));}public String getValue(String key) {String value = l1Cache.getIfPresent(key);if (value == null) {value = l2Cache.getIfPresent(key);if (value != null) {l1Cache.put(key, value);}}return value;}
}
在这个示例中,我们使用两个不同的 Caffeine 缓存实例,分别代表 L1 缓存和 L2 缓存。在 getValue
方法中,我们首先尝试从 L1 缓存中获取数据,如果不存在,则从 L2 缓存中获取数据,并将数据加载到 L1 缓存中。通过这种方式,我们实现了分层的数据存储结构。
综上所述,分层策略适用于需要优化数据访问和管理缓存容量的场景,通过将数据存储在不同层级的缓存中,可以提高数据的访问效率和灵活性。
✍3. 同步监听器
同步监听器是 Caffeine 缓存提供的一种计算策略,它允许你在缓存数据发生更改时触发一些操作,例如更新其他关联数据或触发异步处理。这个策略可以帮助你实现数据变更时的后续处理逻辑,保证数据的一致性和可靠性。以下是对同步监听器策略的详细解释:
作用: 同步监听器策略的主要作用是在缓存数据发生更改时触发一些操作,以实现数据变更时的后续处理逻辑。
使用场景: 同步监听器适用于以下场景:
- 数据更新通知:当缓存中的数据发生更改时,需要及时更新其他关联的数据。
- 异步处理:在数据发生更改时,触发异步任务进行后续处理,如日志记录、通知等。
优缺点:
- 优点:
- 数据一致性:通过同步监听器,可以实现缓存数据和其他数据的一致性维护。
- 后续处理:允许你在数据变更时触发后续处理逻辑,增加了数据的可靠性。
- 缺点:
- 阻塞问题:同步监听器可能导致缓存操作的阻塞,影响性能和响应时间。
- 复杂性增加:需要在监听器中实现合适的处理逻辑,增加了代码的复杂性。
示例代码:
假设有一个需要在数据变更时触发后续处理的服务,我们可以使用 Caffeine 缓存的同步监听器策略来实现。
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class SyncListenerService {private final Cache<String, String> cache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).removalListener((key, value, cause) -> {if (cause.wasEvicted()) {// Cache entry was evicted due to expiration or size limit// Perform additional actions here}}).build();public String getValue(String key) {// Your logic to load data from cache or external resourcereturn cache.get(key, this::loadData);}private String loadData(String key) {// Your logic to load data from external resourcereturn "LoadedValue";}
}
在这个示例中,我们使用了 Caffeine.newBuilder().removalListener(...)
来配置同步监听器。当缓存条目被移除时,监听器的逻辑会被触发,你可以在其中实现相应的后续处理,如记录日志或触发异步任务。
综上所述,同步监听器策略适用于需要在缓存数据变更时触发后续处理逻辑的场景,通过监听缓存操作,可以实现数据的一致性和可靠性维护。
✌6. Interner:
- 提供了一个对象池,用于复用等效的对象以节省内存。
- 类似于
String.intern()
,但更加灵活。
Caffeine 提供的Interner
是一个用于管理共享对象引用的工具,它可以用于限制在一个 JVM 中对象的数量,以便在应用程序中重用相同的实例,从而节省内存。在 Spring Boot 中,你可以使用 Caffeine 的Interner
来优化对象引用的共享和管理。以下是使用 Caffeine 的Interner
的作用、使用场景、优缺点以及适用于 Spring Boot 项目的实际示例代码:
✍作用:
Interner
的作用是维护一组共享的对象引用,以便重用相同的实例,从而节省内存并提高性能。它适用于需要频繁创建和使用对象的场景,通过限制对象实例的数量,避免了创建过多的重复对象。
✍使用场景:
适用于以下场景:
- 频繁创建对象:当应用程序中频繁创建对象时,可以使用
Interner
来共享相同的实例,减少内存占用。 - 对象重用:当对象实例被频繁使用时,可以使用
Interner
来重用这些实例,提高性能。
✍优缺点:
优点:
- 内存节省:通过共享对象实例,可以降低内存占用。
- 性能提升:避免频繁创建对象,减少了垃圾回收和对象初始化的开销。
缺点:
- 不适用于所有场景:并不是所有的对象都适合通过
Interner
进行管理,一些对象可能需要独立实例化。
✍示例代码:
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Interner;
import org.springframework.stereotype.Service;@Service
public class ObjectInternerService {private final Interner<String> stringInterner = Caffeine.newBuilder().buildInterner();public String internString(String value) {return stringInterner.intern(value);}
}
在这个示例中,我们创建了一个 Interner
来共享字符串对象的实例。internString
方法使用 stringInterner
来共享相同值的字符串实例,从而节省内存。这在处理大量相同值字符串时特别有用。
综上所述,Caffeine 提供的 Interner
可以在 Spring Boot 项目中用于优化对象引用的共享和管理。通过使用 Interner
,你可以在需要频繁创建和使用对象的场景中,减少内存占用和性能开销。
✌7. 统计:
- 可以记录缓存的命中率、加载时间等统计信息。
- 使用
recordStats()
方法启用统计。
Caffeine 提供的统计功能可以帮助你监控和分析缓存的使用情况,从而优化缓存配置和性能。在 Spring Boot 中,你可以使用 Caffeine 的统计功能来收集缓存的统计信息,并根据这些信息进行调优。以下是使用 Caffeine 的统计功能的作用、使用场景、优缺点以及适用于 Spring Boot 项目的实际示例代码:
✍作用:
Caffeine 的统计功能可以用于监控和分析缓存的使用情况,包括缓存的命中率、加载次数、剔除次数等。这些统计信息可以帮助你了解缓存的性能表现和数据访问模式,从而做出相应的调整和优化。
✍使用场景:
适用于以下场景:
- 缓存性能优化:通过监控缓存的命中率和加载次数,可以判断缓存是否有效,从而进行性能优化。
- 调优决策:根据统计信息,可以调整缓存的配置参数,如缓存大小、过期时间等,以获得更好的性能。
✍优缺点:
优点:
- 性能优化:通过统计信息,可以针对性地进行性能优化。
- 决策支持:统计信息可以帮助你做出更合理的缓存调优决策。
缺点:
- 额外开销:收集统计信息可能会带来一定的额外开销。
✍示例代码:
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.stereotype.Service;@Service
public class CacheStatisticsService {private final Cache<String, String> cache = Caffeine.newBuilder().maximumSize(100).recordStats() // Enable statistics collection.build();public void addToCache(String key, String value) {cache.put(key, value);}public String getFromCache(String key) {return cache.getIfPresent(key);}public CacheStats getCacheStatistics() {return cache.stats();}
}
在这个示例中,我们使用了 recordStats()
方法来启用统计功能。addToCache
和 getFromCache
方法用于操作缓存,getCacheStatistics
方法用于获取缓存的统计信息。
你可以在 Spring Boot 项目中使用 CacheStatisticsService
来监控缓存的性能和使用情况,并根据统计信息进行性能优化和配置调整。
综上所述,Caffeine 提供的统计功能可以在 Spring Boot 项目中用于监控和优化缓存的性能。通过收集和分析统计信息,你可以更好地了解缓存的表现,从而做出合适的性能优化和决策。
✌8. 规范:
- 定义缓存的配置,如大小、过期时间等。
- 在Spring Boot中,使用
spring.cache.caffeine.spec
来设置规范字符串。
✍作用:
-
CaffeineSpec
的作用是提供一种使用简单的字符格式配置来定义 Caffeine 缓存的各种参数,从而在* 不直接编写代码的情况下配置缓存行为。 -
CaffeineSpec为Caffeine提供了一个简单的字符格式配置。这里的字符串语法是一系列由逗号隔开的键值对组成,其中每个键值对对应一个配置方法。但是这里的字符配置不支持需要对象来作为参数的配置方法,比如 removalListener,这样的配置必须要在代码中进行配置。
-
以下是各个配置键值对字符串所对应的配置方法。将maximumSize和maximumWeight或者将weakValues和weakValues 在一起使用是不被允许的。
配置键 | 配置方法 | 描述 |
---|---|---|
initialCapacity=[integer] | Caffeine.initialCapacity | 设置初始容量 |
maximumSize=[long] | Caffeine.maximumSize | 设置最大缓存条目数量 |
maximumWeight=[long] | Caffeine.maximumWeight | 设置最大缓存权重 |
expireAfterAccess=[持续时间] | Caffeine.expireAfterAccess(Duration) | 设置访问后过期时间 |
expireAfterWrite=[持续时间] | Caffeine.expireAfterWrite(Duration) | 设置写入后过期时间 |
refreshAfterWrite=[持续时间] | Caffeine.refreshAfterWrite(Duration) | 设置写入后刷新时间 |
weakKeys | Caffeine.weakKeys | 启用弱引用键 |
weakValues | Caffeine.weakValues | 启用弱引用值 |
softValues | Caffeine.softValues | 启用软引用值 |
recordStats | Caffeine.recordStats | 启用统计信息收集 |
✍使用场景:
适用于以下场景:
- 简化配置:当需要配置多个 Caffeine 缓存的时候,使用字符串格式的配置可以更简洁地定义缓存的参数。
- 配置可视化:对于不熟悉代码编写的人员,使用字符串配置可以更直观地理解和配置缓存。
✍优缺点:
优点:
- 简化配置:通过字符串格式的配置,可以简化缓存的配置过程。
- 可视化:配置更直观,不需要直接编写代码。
缺点:
- 有限制:某些复杂配置需要在代码中进行,而不支持在字符串中配置。
✍示例代码:
首先,在 Spring Boot 项目的配置文件中添加 Caffeine 缓存的配置,例如 application.properties
:
spring.cache.cache-names=myCache
spring.cache.caffeine.spec=maximumSize=100,expireAfterWrite=30m,recordStats
然后,在你的服务类中使用配置的缓存:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class CacheService {@Cacheable(cacheNames = "myCache", cacheManager = "caffeineCacheManager")public String getCachedValue(String key) {// Simulate fetching value from a data sourcereturn "CachedValue for " + key;}
}
在这个示例中,我们使用了 spring.cache.caffeine.spec
配置来定义缓存的参数,包括最大大小、写入后过期时间和启用统计功能。然后,在 CacheService
类中,我们使用 @Cacheable
注解来应用该缓存配置。
请注意,根据你提供的信息,CaffeineSpec
并不支持在字符串配置中直接配置复杂参数,比如 removalListener
。这类配置仍然需要在代码中进行。
综上所述,CaffeineSpec
可以在 Spring Boot 项目中用于简化和可视化 Caffeine 缓存的配置过程。通过配置字符串,你可以更方便地定义缓存的参数,同时仍然可以通过代码来配置一些复杂的参数。
✍持续时间示例代码
当使用 Caffeine 的配置字符串来表示持续时间时,你可以选择使用不同的格式,包括普通格式和 ISO-8601 标准的格式。下面是不同格式下的示例,用于表示不同的持续时间:
普通格式 | ISO-8601 格式 | 描述 |
---|---|---|
50s | PT50S | 50秒 |
11m | PT11M | 11分钟 |
6h | PT6H | 6小时 |
3d | P3D | 3天 |
P3DT3H4M | 3天3小时4分钟 | |
- | -7小时,-3分钟(不支持) |
这些格式可以用于设置不同的缓存时间参数,如过期时间、刷新时间等,具体取决于你在 spring.cache.caffeine.spec
配置中使用的键值对。在 Spring Boot 项目中,你可以将这些格式中的任何一个用于设置缓存的时间参数。
示例代码中的配置示范了如何在 Spring Boot 项目中使用不同的持续时间格式来配置 Caffeine 缓存参数。根据你的实际需求,你可以选择最适合的持续时间表示方式,以达到优化缓存性能的目标。
下面是示例代码,展示如何使用不同的持续时间表示方法来配置 Caffeine 缓存的参数,以及如何在 Spring Boot 项目中应用这些配置:
# 使用不同的持续时间表示方式配置 Caffeine 缓存
spring.cache.caffeine.spec=expireAfterAccess=50s,refreshAfterWrite=PT11M
然后,在服务类中使用配置的缓存:
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class CacheService {@Cacheable(cacheNames = "myCache", cacheManager = "caffeineCacheManager")public String getCachedValue(String key) {// Simulate fetching value from a data sourcereturn "CachedValue for " + key;}
}
在这个示例中,我们使用了 expireAfterAccess
和 refreshAfterWrite
配置来演示如何使用不同的持续时间表示方法。你可以根据需要在 spring.cache.caffeine.spec
属性中配置其他的缓存参数。
综上所述,通过不同的持续时间表示方法,你可以在 Spring Boot 项目中更灵活地配置 Caffeine 缓存的参数,以满足不同的业务需求和性能要求。
✌9. 清理:
- 清理操作由Caffeine自动处理,确保缓存不会过度消耗资源。
- 使用
cleanUp()
方法可以手动触发清理。
Caffeine 提供了Cache.cleanUp()
方法,用于手动触发缓存的清理操作。虽然 Caffeine 内部会根据配置的清理策略自动进行清理,但在某些场景下,你可能需要手动调用清理操作来立即释放一些缓存资源。以下是Cache.cleanUp()
方法的作用、使用场景、优缺点以及适用于 Spring Boot 项目的实际示例代码:
✍作用:
Cache.cleanUp()
方法的作用是手动触发缓存的清理操作,以立即释放一些缓存资源。这可以帮助你在某些特定情况下控制缓存的内存占用,以及确保过期或无用的缓存条目得到及时清理。
✍使用场景:
适用于以下场景:
- 手动控制清理:在特定操作后,你可能希望立即清理缓存以释放内存。
- 紧急情况:当你需要确保过期的缓存数据被及时清理时,可以手动调用清理操作。
✍优缺点:
优点:
- 灵活性:允许你在需要的时候手动触发缓存的清理,从而更灵活地控制缓存资源。
缺点:
- 过度使用:频繁调用清理操作可能会导致性能下降,应该在合适的时机使用。
✍示例代码:
下面是一个使用 Spring Boot 中的 Caffeine 缓存以及手动触发清理操作的示例代码:
首先,在 Spring Boot 项目的配置文件中配置 Caffeine 缓存:
# application.properties
spring.cache.cache-names=myCache
spring.cache.caffeine.spec=maximumSize=100,expireAfterWrite=30m
然后,在你的服务类中使用缓存和手动触发清理操作:
import com.github.benmanes.caffeine.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class CacheService {@Autowiredprivate Cache<Object, Object> myCache;@Cacheable(cacheNames = "myCache", cacheManager = "caffeineCacheManager")public String getCachedValue(String key) {// Simulate fetching value from a data sourcereturn "CachedValue for " + key;}public void manualCacheCleanup() {myCache.cleanUp(); // 手动触发缓存清理}
}
在这个示例中,我们在 CacheService
中使用 @Cacheable
注解来定义缓存策略。然后,我们通过 myCache.cleanUp()
方法来手动触发缓存的清理操作。
综上所述,Cache.cleanUp()
方法可以在 Spring Boot 项目中使用,帮助你在特定场景下手动触发缓存的清理,以确保缓存资源得到适时释放。
✌10. 策略:
- 内部API,允许插件来修改缓存的行为。
- 一般用于高级用途。
下面我将为你详细解释每个基于容量和基于时间的策略,包括它们的作用、使用场景、优缺点和适用于 Spring Boot 项目的实际示例代码:
✍基于容量的策略
cache.policy().eviction().ifPresent(eviction -> {...});
作用: 通过该策略,你可以调整缓存的最大条目数量,以控制缓存中的数据数量。
使用场景: 适用于需要动态地调整缓存的容量,以适应数据量的变化。
优缺点:
- 优点:允许你根据数据量的变化进行灵活的缓存容量调整。
- 缺点:可能导致频繁的调整,影响性能。
示例代码:
import com.github.benmanes.caffeine.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class CacheService {@Autowiredprivate Cache<Object, Object> myCache;public void adjustCacheCapacity() {myCache.policy().eviction().ifPresent(eviction -> {eviction.setMaximum(2 * eviction.getMaximum()); // 将最大条目数量翻倍});}
}
✍基于时间的策略
cache.policy().expireAfterAccess().ifPresent(expiration -> {...});
cache.policy().expireAfterWrite().ifPresent(expiration -> {...});
cache.policy().expireVariably().ifPresent(expiration -> {...});
cache.policy().refreshAfterWrite().ifPresent(refresh -> {...});
作用: 这些基于时间的策略允许你设置不同的失效时间,以控制缓存数据何时过期或刷新。
使用场景: 适用于需要根据数据的实际使用情况来设置失效时间的场景。
优缺点:
- 优点:可以根据数据的使用频率或写入时间进行动态的失效时间设置。
- 缺点:需要根据业务需求进行合理的设置,否则可能导致缓存数据不准确。
示例代码:
import com.github.benmanes.caffeine.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class CacheService {@Autowiredprivate Cache<Object, Object> myCache;public void adjustExpirationPolicy() {myCache.policy().expireAfterAccess().ifPresent(expiration -> {expiration.setExpiresAfterAccess(30, TimeUnit.MINUTES); // 设置访问后的过期时间为30分钟});myCache.policy().expireAfterWrite().ifPresent(expiration -> {expiration.setExpiresAfterWrite(1, TimeUnit.HOURS); // 设置写入后的过期时间为1小时});// 其他策略类似...}
}
在这个示例中,我们使用不同的基于时间的策略来调整缓存的失效时间。
综上所述,Caffeine 提供了基于容量和基于时间的策略,可以根据不同的业务需求来管理缓存的容量和失效行为。在 Spring Boot 项目中,你可以根据不同的场景选择合适的策略,以优化缓存的性能和资源利用。
✌11. 测试:
- Caffeine提供了测试工具来模拟和验证缓存的行为。
- 可以使用
Ticker
来模拟时间。
Caffeine 提供了在测试中使用的一些策略,例如在测试基于时间的驱逐策略时不需要等待真实时间的推进,以及自定义执行周期维护和异步生成操作。以下是解释这些策略的作用、使用场景、优缺点以及适用于 Spring Boot 项目的实际示例代码:
✍1. 自定义时间源和驱逐策略的测试
🎷作用:
在测试中,当涉及基于时间的缓存驱逐策略时,不需要等待实际时间的推进。你可以使用 Caffeine 的 Ticker
接口和 Caffeine.ticker(Ticker)
方法来定义自定义的时间源,而不必等待系统时钟。
🎷使用场景:
适用于测试基于时间的缓存策略,以模拟时间的推进而不依赖于实际的系统时钟。
🎷优缺点:
优点:
- 可以在测试中更精确地模拟时间的推进,加速测试的执行。
- 减少了测试的等待时间,提高了测试效率。
缺点:
- 需要编写额外的代码来配置和使用自定义的时间源。
🎷示例代码:
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.Ticker;
import org.springframework.cache.Cache;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.stereotype.Service;@Service
public class CacheService {private final Cache<Object, Object> caffeineCache;public CacheService() {Ticker ticker = Ticker.systemTicker(); // 使用系统时间Caffeine<Object, Object> caffeineBuilder = Caffeine.newBuilder().ticker(ticker).expireAfterWrite(1, TimeUnit.HOURS); // 设置缓存写入后的过期时间为1小时caffeineCache = new CaffeineCache("myCache", caffeineBuilder.build());}public String getCachedValue(String key) {// Simulate fetching value from a data sourcereturn (String) caffeineCache.get(key, k -> "CachedValue for " + k);}
}
在这个示例中,我们使用了 Ticker.systemTicker()
来设置自定义的时间源,以模拟时间的推进。
✍2. 自定义执行器的测试
🎷作用:
Caffeine 通过 Executor
来执行周期维护、移除通知和异步生成操作。你可以通过配置一个自定义的执行器来更好地控制这些操作。
🎷使用场景:
适用于在测试中模拟和控制异步操作的执行,以提高测试的可靠性。
🎷优缺点:
优点:
- 可以在测试中更好地模拟异步操作的执行。
- 提高了测试的可靠性,减少了异步操作的不确定性。
缺点:
- 需要额外的配置和代码来使用自定义的执行器。
🎷示例代码:
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic CacheManager caffeineCacheManager() {CaffeineCacheManager cacheManager = new CaffeineCacheManager();cacheManager.setCaffeine(Caffeine.newBuilder().executor(Executors.newSingleThreadExecutor()) // 设置自定义的执行器.expireAfterWrite(1, TimeUnit.HOURS)); // 设置缓存写入后的过期时间为1小时return cacheManager;}
}
在这个示例中,我们使用 Executors.newSingleThreadExecutor()
来设置一个单线程的自定义执行器,以控制异步操作的执行。
综上所述,Caffeine 提供了在测试中使用的策略,可以帮助你更好地模拟和控制缓存的行为,以确保测试的准确性和可靠性。在 Spring Boot 项目中,你可以使用这些策略来优化测试,验证缓存的效果。
✌12. Faq:
Caffeine 提供了一些常见问题的解决方案,涉及固定缓存元素、递归生成和写竞争等问题。以下是解释这些解决方案的作用、使用场景、优缺点以及适用于 Spring Boot 项目的实际示例代码:
✍1. 固定缓存元素
🎷作用:
固定缓存元素是一种特殊的策略,可以让缓存的值保持不变,无论缓存策略是否过期。适用于需要在一段时间内保持特定值的情况,不受过期时间影响。
🎷使用场景:
适用于需要在缓存中存储静态数据或配置信息,并保持这些数据不变的场景。
🎷优缺点:
优点:
- 保持特定值不变,适用于存储静态数据。
- 可以避免频繁的缓存刷新,提高性能。
缺点:
- 不适合动态数据,可能导致数据不准确。
🎷示例代码:
import com.github.benmanes.caffeine.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class CacheService {@Autowiredprivate Cache<Object, Object> myCache;@Cacheable(cacheNames = "myCache", key = "#key")public String getStaticValue(String key) {// Simulate fetching static value from a data sourcereturn "StaticValue";}
}
在这个示例中,我们使用 @Cacheable
注解将静态值缓存起来,无论缓存是否过期,都会返回相同的静态值。
✍2. 递归生成
🎷作用:
递归生成是指在获取缓存值时,如果值不存在,则通过计算生成新的值,并将其存储在缓存中。这样可以避免多个线程同时计算相同的值。
🎷使用场景:
适用于计算开销较大的值,以及需要避免多线程计算冲突的场景。
🎷优缺点:
优点:
- 避免多个线程同时计算相同的值,提高性能。
- 适用于计算开销较大的值。
缺点:
- 可能会导致第一个线程计算的值被其他线程使用,如果这个值是可变的,可能会引起问题。
🎷示例代码:
import com.github.benmanes.caffeine.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class CacheService {@Autowiredprivate Cache<Object, Object> myCache;public String computeValue(String key) {return myCache.get(key, k -> computeExpensiveValue(k));}private String computeExpensiveValue(Object key) {// Simulate expensive computationreturn "ComputedValue for " + key;}
}
在这个示例中,我们使用 myCache.get(key, k -> computeExpensiveValue(k))
来实现递归生成新的值。
✍3. 写竞争
🎷作用:
写竞争是指多个线程同时写入相同的缓存条目,可能导致数据不一致。Caffeine 提供了一些解决方案来处理写竞争问题。
🎷使用场景:
适用于多个线程同时写入相同缓存条目的场景,需要确保数据一致性。
🎷优缺点:
优点:
- 解决了多个线程同时写入缓存的数据不一致问题。
- 提高了数据的一致性和可靠性。
缺点:
- 可能会增加一些额外的开销,例如同步或锁。
🎷示例代码:
import com.github.benmanes.caffeine.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class CacheService {@Autowiredprivate Cache<Object, Object> myCache;public void writeToCache(String key, String value) {synchronized (myCache) {myCache.put(key, value);}}
}
在这个示例中,我们
使用同步块来确保多个线程同时写入缓存时不会产生竞争问题。
综上所述,Caffeine 提供了在 Spring Boot 项目中解决固定缓存元素、递归生成和写竞争等问题的解决方案。这些方案可以帮助你更好地管理和优化缓存的使用。
相关文章:
Springboot - 15.二级分布式缓存集成-Caffeine
👀中文文档 Caffeine 👀使用Caffeine (本地缓存) 当与Spring Boot结合使用时,Caffeine提供了一个直观且功能强大的二级缓存解决方案。Spring Boot的缓存抽象使得整合Caffeine变得相当简单。以下是如何在Spring Boot…...

二叉树的介绍及二叉树的链式结构的实现(C语言版)
前言 二叉树是一种特殊的树,它最大的度为2,每个节点至多只有两个子树。它是一种基础的数据结构,后面很多重要的数据结构都是依靠它来进行实现的。了解并且掌握它是很重要的。 目录 1.二叉树的介绍 1.1概念 1.2现实中的二叉树 1.3特殊的二叉…...

不同写法的性能差异
“ 达到相同目的,可以有多种写法,每种写法有性能、可读性方面的区别,本文旨在探讨不同写法之间的性能差异 len(str) vs str "" 本部分参考自: [问个 Go 问题,字符串 len 0 和 字符串 "" ,有啥区别?](https://segmentf…...

Bytebase 2.7.0 - 新增分支(Branching)功能
🚀 新功能 新增支持与 Git 类似的分支(Branching)功能来管理 schema 变更。支持搜索所有历史工单。支持导出审计日志。 🎄 改进 变更数据库工单详情页面全新改版。优化工单搜索体验。SQL 审核规则支持针对不同数据库进行独立配…...
day55 动规.p15 子序列
- 392.判断子序列 cpp class Solution { public: bool isSubsequence(string s, string t) { vector<vector<int>> dp(s.size() 1, vector<int>(t.size() 1, 0)); for (int i 1; i < s.size(); i) { for (int j 1; …...
TypeScript DOM类型的声明
TS DOM类型的声明 lib.dom.d.ts HTMLInputElement <input type"text" change"handleChange" /> const handleChange (evt: Event) > {console.log((evt.target as HTMLInputElement).value); } HTMLElement const div: HTMLDivElement do…...

springboot找不到注册的bean
1、错误描述 A component required a bean named ‘fixedAssetsShareMapper’ that could not be found.Action:Consider defining a bean named ‘fixedAssetsShareMapper’ in your configuration.2、问题分析 1、该错误提示表明在你的应用程序中有一个组件(可能…...

MEMS传感器的原理与构造——单片式硅陀螺仪
一、前言 机械转子式陀螺仪在很长的一段时间内都是唯一的选项,也正是因为它的结构和原理,使其不再适用于现代小型、单体、集成式传感器的设计。常规的机械转子式陀螺仪包括平衡环、支撑轴承、电机和转子等部件,这些部件需要精密加工和…...

Redis集群服务器
集群简介 试想有一家餐厅,如果顾客人数较少,那么餐厅只需要一个服务员即可,如图1。但是,当顾客人数非常多时,一个服务员是绝对不够的,如图2。此时,餐厅需要雇用更多的服务员来解决大量访问&…...

动态维护直径 || 动态维护树上路径 || 涉及LCA点转序列 || 对欧拉环游序用数据结构维护:1192B
https://www.luogu.com.cn/problem/CF1192B 对于直径的求法,常用dp或两次dfs,但如果要动态维护似乎都不太方面,那么可以维护树上路径最大值。 树上路径为: d e p u d e p v − 2 d e p l c a ( u , v ) dep_udep_v-2\times de…...

MySQL 存储引擎,你了解几个?
引言 MySQL是一种流行的关系型数据库管理系统(RDBMS),它支持多种不同的数据库引擎。数据库引擎是用于存储、管理和检索数据的核心组件,它们直接影响着数据库的性能、可靠性和功能,接下来本文介绍下一些常见的MySQL数据…...

Java 动态规划 Leetcode 740. 删除并获得点数
题目 对于该题的题目分析,已经代码分析都一并写入到了代码注释中 代码 class Solution {public int deleteAndEarn(int[] nums) {//核心思路://由于我们获得 nums[i] 的点数之后,就必须删除所有等于 nums[i] - 1 和 nums[i] 1 的元素//假设…...
算法通关村十三关-青铜:数字与数学基础问题
1.数字统计专题 统计特定场景下的符号或数字个数等 1.1符号统计 LeetCode1822 数组元素积的符号 https://leetcode.cn/problems/sign-of-the-product-of-an-array/description/ 思路分析 如果将所有的数都乘起来,再判断正负,工作量大,还…...

猜拳游戏小程序源码 大转盘积分游戏小程序源码 积分游戏小程序源码
简介: 猜拳游戏大转盘积分游戏小程序前端模板源码,一共五个静态页面,首页、任务列表、大转盘和猜拳等五个页面 图片:...

【Python】爬虫练习-爬取豆瓣网电影评论用户的观影习惯数据
目录 前言 一、配置环境 1.1、 安装Python 1.2、 安装Requests库和BeautifulSoup库 1.3.、安装Matplotlib 二、登录豆瓣网(重点) 2.1、获取代理 2.2、测试代理ip是否可用 2.3、设置大量请求头随机使用 2.4、登录豆瓣网 三、爬取某一部热门电影…...
webpack基础配置【总结】
webpack打包原理: webpack是一个js应用程序的静态模块打包工具,当webpack处理应用程序时,它的内部构建一个依赖图,此时依赖会映射项目中所需的每个模块,并生成一个或多个bundle包。因此我们会安装配置各种打包规则&…...

typescript 支持与本地调试
typescript 支持与本地调试 typescript 支持与本地调试 前言支持 typescript函数的本地调试 启用 node-terminal 调试invoke localserverless-offline Next Chapter完整示例及文章仓库地址 前言 在上一章节,我们创建了一个 hello world 函数,并把它顺…...
后端面试话术集锦第 十八 篇:JVM面试话术
这是后端面试集锦第十八篇博文——JVM面试话术❗❗❗ 1. 介绍下JVM JVM主要包括:类加载器(class loader)、执行引擎(exection engine)、本地接口(native interface)、运行时数据区(Runtimedata area) 类加载器:加载类文件到内存。Class loader只管加载,只要符合文件…...

“历久弥新 | 用AI修复亚运珍贵史料”活动震撼来袭!
时隔近半个世纪,新中国第一次参与亚运会的影像资料将首次对外披露。只是年代久远,老照片老视频都有了岁月痕迹,画面不再清晰,这些珍贵史料急需你的帮助! 一、活动介绍 2023年,正值亚运110周年,…...
uni-app 之 scroll-view和swiper
uni-app 之 scroll-view和swiper <!-- vue2的<template>里必须要有一个盒子,不能有两个,这里的盒子就是 view--> <template><view><navigator url"/pages/home/home"><button style"background: #ff00f…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...