【本地缓存篇】如何实现本地缓存?

2023-12-28 19:18:12

在这里插入图片描述


??典型解析


所谓本地缓存,就是和应用服务器在一起的缓存工具,将需要缓存的数据放到本地缓存中,可以大大的提升访问速度。


在设计本地缓存时,一般需要考虑以下几个方面的问题:


??数据结构


一般来讲,为了提升缓存的效率,通常采用Key-Value结构进行数据存储,也就是说,缓存中的数据保存和读取都需要有一个Key,通过Key来读取固定的缓存的Value。

??线程安全


本地缓存一定要考虑线程安全的问题,因为大多数情况下本地缓存都是一个全局可访问的变量,那么就会有多个线程同时访问,所以线程安全问题不容忽视。


??对象上限


因为是本地缓存,而本地内存中的数据是要占用JVM的堆内存的,所以内存是有上限要求的,如果无限存储,最终一定会导致OOM的问题。


??清除策略


为了避免OOM的问题,一般会考虑在缓存中增加清除策略,通过一定的手段定期的清理掉一些数据,来保证内存占用不会过大,常见清除策略主要有有LRU(最近最少使用)、FIFO(先进先出)、LFU(最近最不常用)、SOFT(软用)、WEAK(弱用)等;


??过期时间


有了清除策略并不能保证百分百的可以删除数据,极端情况会会使得某些数据一直无法删除。这时候就需要有一种机制能够保证某些K-V一定可以删除。通常采用的方案是给每一个缓存的kev设置过期时间,当达到过期时间之后直接删除,采用清除策略+过期时间双重保证;


考虑到以上这些问题之后,就可以考虑如何具体实现一个本地缓存了。


最简单的方式是通过HashMap来实现一个本地缓存,因为他本身就是一种Key-Value结构的,并且如果使用ConcurrentHashMap的话,也能保证线程安全,不过需要自己实现对象上限、过期策略以及清除策略。


除此之外,也有一些比较成熟的开源的本地缓存框架可以直接使用,比较常用的有:


  • Guava Cache
  • Caffeine (推荐)
  • Encache

推荐优先使用Caffeine作为本地缓存,在功能上,GuavaCache支持的功能,Caffeine都支持,另外Caffeine支持异步Cache和写入外部资源,这两个Guava Cache是不支持的。Caffeine也是Spring 5中默认支持的Cache。而Caffeine在性能上要比GuavaCache好很多,主要有以下几个原因:


1 . 剔除算法,GuavaCache采用的是 [LRU] 算法,而Caffeine采用的是[Window TinyLFU] 算法,这是两者之间最大,也是根本的区别。


2 . 立即失效,Guava会把立即失效(例如: expireAfterAccess(0) and expireAfterWrite(0)) 转成设置最大Size为0。这就会导致剔除提醒的原因是SIZE而不是EXPIRED。Caffiene能正确识别这种剔除原因。


3 . 取代提醒,Guava只要数据被替换,不管什么原因,都会触发剔除监听器。而Cafiene在取代值和先前值的引用完全一样时不会触发监听器。


4 . 异步化,Caffiene的很多工作都是交给线程池去做的(默认: ForkJoinPool.commonPool()),例如: 剔除监听器,刷新机制,维护工作等


回顾博文: 【本地缓存篇】LFU、LRU 等缓存失效算法


??扩展知识仓


基于Caffeine实现本地缓存


import com.github .benmanes .caffeine.cache.Cache;
import com.github.benmanes .caffeine.cache.Caffeine;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;


import java.util.concurrent.TimeUnit;

/**
*    本地缓存工具
*     @author wwwwwwwwwwwwwwwwwwww
*/
@Component
public class LocalCacheManager implements InitializingBean {
	private Cache<String,String> localCache;

	/**
	*    向缓存中保存数据,如果已经存在则不覆盖
	*/
	public void putIfNotExist(String key,String value)  {
		if (localCache.getIfPresent(key) == null)  {
			localCache.put(key, value);
		}
	}

	/**
	*
	*     根据key获取缓存数据
	*     @param key
	*/
	public String get(String key)  {
		return localCache.getIfPresent(key);
	}

	public void del(string key) {
		localCache.invalidate(key);
	}

	/**
	*    在bean初始化时,初始化本地缓存
	*/
	@Override
	public void afterPropertiesSet() {
		localCache = Caffeine.newBuilder()
		.expireAfterWrite(10TimeUnit.SECONDS)
		.expireAfterAccess(10TimeUnit.SECONDS)
		.maximumsize(1000)
		.build();
		
	}

	
}

文章来源:https://blog.csdn.net/Java_Yangxiaoyuan/article/details/135273648
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。