关于java中的本地缓存-相关实现方式

2021/8/24 22:06:13

本文主要是介绍关于java中的本地缓存-相关实现方式,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

java中的本地缓存,工作后陆续用到,一直想写,一直无从下手,最近又涉及到这方面的问题了,梳理了一下。自己构造单例、guava、ehcache基本上涵盖了目前的大多数行为了。
为什么要有本地缓存?

在系统中,有些数据,数据量小,但是访问十分频繁(例如国家标准行政区域数据),针对这种场景,需要将数据搞到应用的本地缓存中,以提升系统的访问效率,减少无谓的数据库访问(数据库访问占用数据库连接,同时网络消耗比较大),但是有一点需要注意,就是缓存的占用空间以及缓存的失效策略。

为什么是本地缓存,而不是分布式的集群缓存?

目前的数据,大多是业务无关的小数据缓存,没有必要搞分布式的集群缓存,目前涉及到订单和商品的数据,会直接走DB进行请求,再加上分布式缓存的构建,集群维护成本比较高,不太适合紧急的业务项目。
在这里插入图片描述本地缓存在那个区域?

目前考虑的是占用了JVM的heap区域,再细化一点的就是heap中的old区,目前的数据量来看,都是一些小数据,加起来没有几百兆,放在heap区域最快最方便。后期如果需要放置在本地缓存的数据大的时候,可以考虑在off-heap区域,但是off-heap区域的话,需要考虑对象的序列化(因为off-heap区域存储的是二进制的数据),另外一个的话就是off-heap的GC问题。其实,如果真的数据量比较大,那其实就可以考虑搞一个集中式的缓存系统,可以是单机,也可以是集群,来承担缓存的作用。

搞一个单例模式,里面有个Map的变量来放置数据

非常典型的代码如下:

 public  class  SingletonMap {
 //一个本地的缓存Map
 private  Map<String,Object> localCacheStore =  new  HashMap<String,Object>(); 
 //一个私有的对象,非懒汉模式
 private  static  SingletonMap singletonMap =  new  SingletonMap(); 

 //私有构造方法,外部不可以new一个对象
 private  SingletonMap(){
 }  

 //静态方法,外部获得实例对象
 public  static  SingletonMap getInstance(){
     return  singletonMap;
 }

 //获得缓存中的数据
 public  Object getValueByKey(String key){
     return  localCacheStore.get(key);
 }
 //向缓存中添加数据
 public  void  putValue(String key , Object value){
     localCacheStore.put(key, value);
 }

}
这种能不能用?可以用,但是非常局限

但是这种的就是本地缓存了吗? 答案显然不是,为啥呢?
1 、  没有缓存大小的设置,无法限定缓存体的大小以及存储数据的限制(max size limit);
2 、  没有缓存的失效策略(eviction policies);
3 、  没有弱键引用,在内存占用吃紧的情况下,JVM是无法回收的(weak rererences keys);
4 、  没有监控统计(statistics);
5 、  持久性存储(persistent store);
所以,这种就直接废掉了。。。
引入EhCache来构建缓存(详细介绍:  http://raychase.iteye.com/blog/1545906)

EhCahce的核心类:

A、CacheManager:Cache的管理类;

B、Cache:具体的cache类信息,负责缓存的get和put等操作

C、CacheConfiguration :cache的配置信息,包含策略、最大值等信息

D、Element:cache中单条缓存数据的单位

典型的代码如下:

public  static  void  main(String[] args) {
     //EhCache的缓存,是通过CacheManager来进行管理的
     CacheManager cacheManager = CacheManager.getInstance();
     
     //缓存的配置,也可以通过xml文件进行
     CacheConfiguration conf =  new  CacheConfiguration();
     conf.name( "cache_name_default" ); //设置名字
     conf.maxEntriesLocalHeap( 1000 ); //最大的缓存数量
     conf.memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LRU); //设置失效策略
     
     //创建一个缓存对象,并把设置的信息传入进去
     Cache localCache =  new  Cache(conf);
     
     //将缓存对象添加到管理器中
     cacheManager.addCache(localCache);
             
     localCache.put( new  Element( "iamzhongyong" ,  new  Date()));
     
     System.out.println(localCache.getSize());
     System.out.println(localCache.getStatistics().toString());
     System.out.println(localCache.getName());
     System.out.println(localCache.get( "iamzhongyong" ).toString());
     System.out.println(localCache.get( "iamzhongyong" ).getObjectValue());   
 }

当然,Cache的配置信息,可以通过配置文件制定了。。。

优点:功能强大,有失效策略、最大数量设置等,缓存的持久化只有企业版才有,组件的缓存同步,可以通过jgroup来实现

缺点:功能强大的同时,也使其更加复杂

引入guava的cacheBuilder来构建缓存

这个非常强大、简单,通过一个CacheBuilder类就可以满足需求。

缺点就是如果要组件同步的话,需要自己实现这个功能。

典型的代码如下:

public  class  GuavaCacheBuilderTest {
     public  static  void  main(String[] args)  throws  Exception{
         GuavaCacheBuilderTest cache =  new  GuavaCacheBuilderTest();
         cache.getNameLoadingCache( "bixiao" );
     }
     public  void  getNameLoadingCache(String name)  throws  Exception{
         LoadingCache<String, String> cache = CacheBuilder.newBuilder()       
             .maximumSize( 20 ) //设置大小,条目数        
             .expireAfterWrite( 20 , TimeUnit.SECONDS) //设置失效时间,创建时间      
             .expireAfterAccess( 20 , TimeUnit.HOURS)  //设置时效时间,最后一次被访问       
             .removalListener( new  RemovalListener<String, String>() {  //移除缓存的监听器
                 public  void  onRemoval(RemovalNotification<String, String> notification) {
                     System.out.println( "有缓存数据被移除了" );
                 }})
             .build( new  CacheLoader<String, String>(){  //通过回调加载缓存
                 @Override
                 public  String load(String name)  throws  Exception {
                     return  name +  "-"  +  "iamzhongyong" ;
                 }
         });
         System.out.println(cache.get(name));
         //cache.invalidateAll();
     }
}

缓存预热怎么搞?

A、全量预热,固定的时间段移除所有,然后再全量预热

适用场景:

1、数据更新不频繁,例如每天晚上3点更新即可的需求;
 2、数据基本没有变化,例如全国区域性数据;
B、增量预热(缓存查询,没有,则查询数据库,有则放入缓存)

适用场景:
1、 数据更新要求缓存中同步更新的场景

​集群内部,缓存的一致性如何保证?

如果采用ehcache的话,可以使用框架本身的JGroup来实现组内机器之间的缓存同步。

如果是采用google的cacheBuilder的话,需要自己实现缓存的同步。

A、非实时生效数据:数据的更新不会时时发生,应用启动的时候更新即可,然后定时程序定时去清理缓存;

B、需要实时生效数据:启动时可预热也可不预热,但是缓存数据变更后,集群之间需要同步


这篇关于关于java中的本地缓存-相关实现方式的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程