java本地缓存简介
创始人
2025-05-31 10:21:55
0

60247376e4a34b69970092896e464193.jpgjava中的本地缓存,工作后陆续用到,一直想写,一直无从下手,最近又涉及到这方面的问题了,梳理了一下。自己构造单例、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、需要实时生效数据:启动时可预热也可不预热,但是缓存数据变更后,集群之间需要同步

 

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
JAVA多线程知识整理 Java多线程基础 线程的创建和启动 继承Thread类来创建并启动 自定义Thread类的子类&#...
【洛谷 P1090】[NOIP... [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G ...
国民技术LPUART介绍 低功耗通用异步接收器(LPUART) 简介 低功耗通用异步收发器...
城乡供水一体化平台-助力乡村振... 城乡供水一体化管理系统建设方案 城乡供水一体化管理系统是运用云计算、大数据等信息化手段࿰...
程序的循环结构和random库...   第三个参数就是步长     引入文件时记得指明字符格式,否则读入不了 ...
中国版ChatGPT在哪些方面... 目录 一、中国巨大的市场需求 二、中国企业加速创新 三、中国的人工智能发展 四、企业愿景的推进 五、...
报名开启 | 共赴一场 Flu... 2023 年 1 月 25 日,Flutter Forward 大会在肯尼亚首都内罗毕...
汇编00-MASM 和 Vis... Qt源码解析 索引 汇编逆向--- MASM 和 Visual Studio入门 前提知识ÿ...
【简陋Web应用3】实现人脸比... 文章目录🍉 前情提要🌷 效果演示🥝 实现过程1. u...
前缀和与对数器与二分法 1. 前缀和 假设有一个数组,我们想大量频繁的去访问L到R这个区间的和,...
windows安装JDK步骤 一、 下载JDK安装包 下载地址:https://www.oracle.com/jav...
分治法实现合并排序(归并排序)... 🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨...
在linux上安装配置node... 目录前言1,关于nodejs2,配置环境变量3,总结 前言...
Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
Linux内核进程管理并发同步... 并发同步并发 是指在某一时间段内能够处理多个任务的能力,而 并行 是指同一时间能够处理...
opencv学习-HOG LO... 目录1. HOG(Histogram of Oriented Gradients,方向梯度直方图)1...
EEG微状态的功能意义 导读大脑的瞬时全局功能状态反映在其电场结构上。聚类分析方法一致地提取了四种头表面脑电场结构ÿ...
【Unity 手写PBR】Bu... 写在前面 前期积累: GAMES101作业7提高-实现微表面模型你需要了解的知识 【技...