关于java中的本地缓存-总结概述

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

 

为什么要有本地缓存?


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

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

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

这里介绍一下缓存使用的三个阶段(摘自info架构师文档)

本地缓存在那个区域?

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

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

关于单例模式,一个既简单又复杂的模式(http://iamzhongyong.iteye.com/blog/1539642

非常典型的代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25


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

2

3

4

5

6

7

8


但是这种的就是本地缓存了吗?答案显然不是,为啥呢?

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中单条缓存数据的单位

典型的代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24


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类就可以满足需求。

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

典型的代码如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24


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、需要实时生效数据:启动时可预热也可不预热,但是缓存数据变更后,集群之间需要同步

至此,告一段落。。。。

时间: 2024-12-20 01:17:58

关于java中的本地缓存-总结概述的相关文章

java应用中的本地缓存

java中的本地缓存,工作后陆续用到,一直想写,一直无从下手,最近又涉及到这方面的问题了,梳理了一下.自己构造单例.guava.ehcache基本上涵盖了目前的大多数行为了. 为什么要有本地缓存?在 系统中,有些数据,数据量小,但是访问十分频繁(例如国家标准行政区域数据),针对这种场景,需要将数据搞到应用的本地缓存中,以提升系统的访问效率,减 少无谓的数据库访问(数据库访问占用数据库连接,同时网络消耗比较大),但是有一点需要注意,就是缓存的占用空间以及缓存的失效策略. 所谓的本地混存是相对于网络

Java基础知识强化10:Java中的中间缓存变量机制

1.对于自增运算++j与j++,由于加一的执行顺序不同,所以Java中有中间缓存变量来储存其单个表达式的值,而j的自增自减的结果依然保留在原来的变量储存区.因为本体是j的值,而单个表达式的值是中间产生的一个临时变量值,是在整条计算表达式结束后就可以抛弃的值,所以用个临时中间缓存变量在放就可以了.这就可以实现自增自减运算在计算时值的加减1顺序差异产生的表达式与本体值差异的两个变量储存. 2. 1 因为在计算过程中,使用了Java中间变量缓存机制.在java中,执行自增运算时,会为每一个自增操作分配

Java中的中间缓存变量机制

上代码: public static void main(String[] args) { int j = 0; for (int i = 0; i < 100; i++) { j = j++; System.out.println(j); } System.out.println(j); } 如果按照Cpp的理解,这个结果应该就是100.不过java,是0. 是因为java中有关自增自减操作执行的,java会通过分配临时变量来执行的.如:j = j++, 是这么执行的: int temp =

【翻译】Integer Cache In Java(在Java中的Integer缓存机制)

返回主页 回到顶端 This Java article is to introduce and discuss about Integer Cache. 这篇Java文章将介绍和讨论整数缓存. This is a feature introduced in Java 5 to save memory and improve the performance. 这是Java 5中引入的一个特性,用于节省内存和提高性能. Let us first have a look at a sample cod

Java中使用memcache缓存

一.简要概述: memcache集群环境下缓存解决方案,是一个高性能的分布式的内存对象缓存系统,通过在内存库里维护一个统一的巨大的hash表,存储各种数据,包括图像.视频.文件以及数据库检索结果等.简单的说就是讲数据调用到存储中,后从内存中读取,从而大大提高读取速度.memcache是这个项目的名称,而memcached是它的服务端的主程序文件名. 二.常用方法: 1.初始化memcache //初始化memcache public static void initMemcache(){ Str

Java中自己定义缓存方式

说说自己在开发中经常用到的写法. /** * 数据缓存 * @author * */public class DataCache {    /** 对象缓存*/    public static Map<String ,MsgPojo>  msgMap = new ConcurrentHashMap<String , MsgPojo>();    } 说明:ConcurrentHashMap 线程安全 这样就定义了一个缓存.还是很简单那啊. 写到这里,音乐想起了<小镇姑娘&g

JAVA中具有实例缓存的不可变类

不可变类的实例的状态不会变化,这样的实例可以安全的被其他与之关联的对象共享,还可以安全的被多个线程共享. 为了节省内存空间, 优化程序的性能, 应该尽可能的重用不可变类的实例,避免重复创建具有相同属性值的不可变类的实例. 在 JDK1.5 的基本类库中, 对一些不可变类, 如 Integer 类作了优化, 它具有一个实例缓存,用来存放程序中经常使用的 Integer 实例.JDK1.5 的 Integer 类新增了一个参数为 int 类型的静态工厂方法 valueOf(int i),它的处理流程

从源码看java中Integer的缓存问题

在开始详细的说明问题之前,我们先看一段代码 1 public static void compare1(){ 2 Integer i1 = 127, i2 = 127, i3 = 128, i4 = 128; 3 System.out.println(i1 == i2); 4 System.out.println(i1.equals(i2)); 5 System.out.println(i3 == i4); 6 System.out.println(i3.equals(i4)); 7 } 这段代

java 中读取本地文件中字符

java读取txt文件内容.可以作如下理解: 首先获得一个文件句柄.File file = new File(); file即为文件句柄.两人之间连通电话网络了.接下来可以开始打电话了. 通过这条线路读取甲方的信息:new FileInputStream(file) 目前这个信息已经读进来内存当中了.接下来需要解读成乙方可以理解的东西 既然你使用了FileInputStream().那么对应的需要使用InputStreamReader()这个方法进行解读刚才装进来内存当中的数据 解读完成后要输出