什么是本地缓存
本地缓存是指将数据存储在应用程序的本地内存中,以便快速访问和提高性能。它是应用程序内部的一种数据缓存机制,用于临时存储经常访问或计算的数据,以避免频繁地从慢速数据源(如数据库或网络)获取数据。
本地缓存通常用于提高数据访问性能,但它并不提供数据的持久性和安全性。
本地缓存的工作原理是:当应用程序需要某个数据时,首先检查本地缓存中是否存在该数据。如果存在,则直接从缓存中获取数据,避免了对慢速数据源的访问。如果缓存中不存在所需数据,应用程序会从数据源获取数据,并将其存储在缓存中,以便下次快速访问。
本地缓存的优势包括:
- 快速访问:数据存储在应用程序的内存中,读取速度快,可以提高数据访问的响应速度。
- 减少资源消耗:避免频繁访问慢速数据源,减少网络带宽、数据库连接等资源的消耗。
- 改善用户体验:提供实时的数据访问,不需要等待远程数据源的响应时间,提高应用程序的性能和用户体验。
- 支持离线访问:在无法连接到远程数据源时,仍能从本地缓存中获取数据,确保应用程序的基本功能可用。
然而,本地缓存也需要注意合理配置缓存大小和生命周期,以避免占用过多内存或数据不一致的问题。
常用的Java缓存库
- Caffeine:Caffeine是一个高性能的内存缓存库,提供了灵活的配置选项和丰富的功能,例如大小基于淘汰、时间基于淘汰、统计信息等。
- Ehcache:Ehcache是一个流行的Java缓存库,提供了广泛的功能和配置选项。它支持内存和磁盘缓存,具有强大的缓存管理和监控功能。
- Guava Cache:Guava Cache是Google Guava库中的一部分,提供了简单易用的内存缓存功能。它支持基于大小的淘汰策略、时间基于淘汰策略和手动清除等。
- Redisson:Redisson是一个基于Redis的分布式缓存库,提供了高性能的分布式缓存解决方案。它支持多种数据结构和功能,例如缓存、分布式锁、分布式集合等。
- Hazelcast:Hazelcast是一个开源的内存数据网格平台,提供了分布式缓存和计算功能。它具有可扩展性和高可用性,适用于大规模和分布式环境。
这些缓存库都具有不同的特点和适用场景,可以根据具体的需求选择合适的缓存库来管理应用程序中的缓存数据。
常见本地缓存库的优缺点及使用场景
以下是常见Java缓存库的优缺点及适用场景:
-
Caffeine:
- 优点:高性能、低延迟、灵活的配置选项、丰富的功能(如大小基于淘汰、时间基于淘汰、统计信息等)。
- 缺点:不支持分布式缓存。
- 适用场景:适用于单机环境下的高性能缓存需求,特别是对缓存性能和灵活性要求较高的场景。
-
Ehcache:
- 优点:功能丰富、广泛使用、支持内存和磁盘缓存、强大的缓存管理和监控功能。
- 缺点:在高并发环境下性能可能较低。
- 适用场景:适用于需要全面功能和管理工具的企业级应用程序,对性能要求不是特别高的场景。
-
Guava Cache:
- 优点:简单易用、功能完善、支持基于大小和时间的淘汰策略、提供手动清除功能。
- 缺点:不支持分布式缓存。
- 适用场景:适用于简单的内存缓存需求,特别是对简单使用和配置要求较高的场景。
-
Redisson:
- 优点:基于Redis的分布式缓存解决方案、高性能、支持多种数据结构和功能、可扩展性和高可用性。
- 缺点:需要依赖Redis作为数据存储,增加了系统复杂性。
- 适用场景:适用于分布式环境下的高性能缓存需求,特别是需要分布式特性和多种数据结构支持的场景。
-
Hazelcast:
- 优点:分布式缓存和计算功能、可扩展性和高可用性、支持多种数据结构和功能。
- 缺点:较复杂的配置和管理、对于小规模应用可能过于重量级。
- 适用场景:适用于需要分布式缓存和计算能力的大规模应用,特别是对分布式和高可用性要求较高的场景。
根据具体的需求和环境,选择适合的缓存库非常重要。需要综合考虑性能、功能、分布式特性、易用性等因素来做出决策。
Caffeine示例
/**
* Abstract Cache Service Defination with caffeine
*/
public abstract class AbstractCache<T> implements RemovalListener<String, T>, CacheLoader<String, T> {
private LoadingCache<String, T> loadingCache;
/**
* @param timeout
* @param initCapacity
* @param maxSize
*/
protected AbstractCache(Duration timeout, int initCapacity, int maxSize) {
this.loadingCache = Caffeine.newBuilder()
//.weakKeys().weakValues()
//expire time after write cache
.expireAfterWrite(timeout.getSeconds(), TimeUnit.SECONDS)
//set initial capacity
.initialCapacity(initCapacity)
//set max size of cache, if cache size > maxnumSize, then use caffine LRU
.maximumSize(maxSize)
//set removal listener
.removalListener(this)
//load data to cache
.build(this);
}
/**
* Get value by key
* */
public T get(String key) throws ExecutionException {
return loadingCache.get(key);
}
/**
* put data
* @param key
* @param val
*/
public void put(String key,T val){
loadingCache.put(key,val);
}
/**
* put all the data
* @param map
*/
public void putAll(Map map){
loadingCache.putAll(map);
}
/**
* remove data
* @param key
*/
public void remove(String key){
loadingCache.invalidate(key);
}
/**
* 防止缓存到指定时间不过期
*/
public T getIfPresent(String key){
return loadingCache.getIfPresent(key);
}
}
@Slf4j
@Service
public class BizLineImplLimitLocalCache extends AbstractCache<Integer> {
public BizLineImplLimitLocalCache() {
super(Duration.ofMinutes(5), 50, 200);
}
@Override
public @Nullable Integer load(@NonNull String key) throws Exception {
return null;
}
@Override
public void onRemoval(@Nullable String key, @Nullable Integer value, @NonNull RemovalCause cause) {
}
}
@Autowired
private BizLineImplLimitLocalCache bizLineImplLimitLocalCache;
bizLineImplLimitLocalCache.put(key, value);
bizLineImplLimitLocalCache.get(key);