zoukankan      html  css  js  c++  java
  • java中的本地缓存

    java中的本地缓存,工作后陆续用到,一直想写,一直无从下手,最近又涉及到这方面的问题了,梳理了一下。自己构造单例、guava、ehcache基本上涵盖了目前的大多数行为了。
     
    为什么要有本地缓存?
    在系统中,有些数据,数据量小,但是访问十分频繁(例如国家标准行政区域数据),针对这种场景,需要将数据搞到应用的本地缓存中,以提升系统的访问效率,减少无谓的数据库访问(数据库访问占用数据库连接,同时网络消耗比较大),但是有一点需要注意,就是缓存的占用空间以及缓存的失效策略。
     
    为什么是本地缓存,而不是分布式的集群缓存?
             目前的数据,大多是业务无关的小数据缓存,没有必要搞分布式的集群缓存,目前涉及到订单和商品的数据,会直接走DB进行请求,再加上分布式缓存的构建,集群维护成本比较高,不太适合紧急的业务项目。
             这里介绍一下缓存使用的三个阶段(摘自info架构师文档)
              
     
    本地缓存在那个区域?
             目前考虑的是占用了JVM的heap区域,再细化一点的就是heap中的old区,目前的数据量来看,都是一些小数据,加起来没有几百兆,放在heap区域最快最方便。后期如果需要放置在本地缓存的数据大的时候,可以考虑在off-heap区域(direct-memory 或者 big-memory),但是off-heap区域的话,需要考虑对象的序列化(因为off-heap区域存储的是二进制的数据),另外一个的话就是off-heap的GC问题。其实,如果真的数据量比较大,那其实就可以考虑搞一个集中式的缓存系统,可以是单机,也可以是集群,来承担缓存的作用。
     
    搞一个单例模式,里面有个Map的变量来放置数据
    关于单例模式,一个既简单又复杂的模式(http://iamzhongyong.iteye.com/blog/1539642)
    非常典型的代码如下:
    public class SingletonMap {
        //一个本地的缓存Map
        private Map localCacheStore = new HashMap(); 
     
        //一个私有的对象,非懒汉模式
        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 cache = CacheBuilder.newBuilder()       
                .maximumSize(20)//设置大小,条目数        
                .expireAfterWrite(20, TimeUnit.SECONDS)//设置失效时间,创建时间      
                .expireAfterAccess(20, TimeUnit.HOURS) //设置时效时间,最后一次被访问       
                .removalListener(new RemovalListener() { //移除缓存的监听器
                    public void onRemoval(RemovalNotification notification) {
                        System.out.println("有缓存数据被移除了");
                    }})
                .build(new CacheLoader(){ //通过回调加载缓存
                    @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实现 蓝桥杯VIP 算法训练 字符删除
    Java实现 蓝桥杯VIP 算法训练 字符删除
    Java实现 蓝桥杯VIP 算法训练 字符删除
    Java实现 蓝桥杯VIP 算法训练 字符删除
    Java实现 蓝桥杯VIP 算法训练 字符删除
    Java实现 蓝桥杯VIP 算法训练 字符串编辑
    Java实现 蓝桥杯VIP 算法训练 字符串编辑
    Java实现 蓝桥杯VIP 算法训练 字符串编辑
    Java实现 蓝桥杯VIP 算法训练 字符串编辑
    Java实现 蓝桥杯VIP 算法训练 字符串编辑
  • 原文地址:https://www.cnblogs.com/lyh971134228/p/6874432.html
Copyright © 2011-2022 走看看