EhCache缓存学习(转)

在看hibernate的官方文档时,看到关于缓存的介绍。

hibernate在缓存管理上做的很好,具体使用,本章不做讲解,本篇主要研究EhCache的用法。其中hibernate使用到的缓存提供商列表如下:

 

CacheProvider classTypeCluster SafeQuery Cache Supported
Hashtable (not intended for production use) org.hibernate.cache.HashtableCacheProvider memory   yes
EHCache org.hibernate.cache.EhCacheProvider memory, disk, transactional, clustered yes yes
OSCache org.hibernate.cache.OSCacheProvider memory,disk   yes
SwarmCache org.hibernate.cache.SwarmCacheProvider clustered (ip multicast) yes (clustered invalidation)  
JBoss Cache 1.x org.hibernate.cache.TreeCacheProvider clustered (ip multicast), transactional yes (replication) yes (clock sync req.)
JBoss Cache 2 org.hibernate.cache.jbc.JBossCacheRegionFactory clustered (ip multicast), transactional yes (replication or invalidation) yes (clock sync req.)


其中,我对EHCache比较感兴趣。看它支持的类型包括对内存,硬盘,传统,集群都支持。

 

我们可以单独研究一下Ehcache缓存的使用,这样方便以后我们对其他使用到缓存的地方进行缓存的自定义管理(不单单在hibernate查询数据方面)。

 

ehcache下载地址:   http://sourceforge.net/projects/ehcache/files/ehcache/  

先写个例子,看看它的api如何使用:

 

EhcacheTest

 

package org.base.cache.test;

import java.net.MalformedURLException;
import java.net.URL;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.Configuration;

/**
 * Ehcache缓存管理的api测试小例子
 * @author lushuaiyin
 *
 */
public class EhcacheTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws MalformedURLException {
		net.sf.ehcache.config.Configuration config=new Configuration();
		
		//如果不使用ehcache.xml配置文件,那么必须用代码配置一个defaultCacheConfiguration
		CacheConfiguration defaultCacheConfiguration=new CacheConfiguration();
		defaultCacheConfiguration.setMaxEntriesLocalHeap(0);
		defaultCacheConfiguration.setEternal(false);
		defaultCacheConfiguration.setTimeToIdleSeconds(30);
		defaultCacheConfiguration.setTimeToLiveSeconds(30);
		
		config.addDefaultCache(defaultCacheConfiguration);//设置默认cache
		
		net.sf.ehcache.CacheManager cacheManager=CacheManager.create(config);
		
		//创建缓存信息
		/*构造方法有多种,详见文档
		 public Cache(String name,
             int maxElementsInMemory,
             boolean overflowToDisk,
             boolean eternal,
             long timeToLiveSeconds,
             long timeToIdleSeconds)
		 */
		//自定义配置缓存
		net.sf.ehcache.Cache cache1=new Cache("mycache-one", 1000, false, false, 30, 30);
		cacheManager.addCache(cache1);
		
		//只有配置了defaultCacheConfiguration,这个方法才可以使用。因为用字符串命名的缓存必须有实际配置。
		cacheManager.addCache("mycache-two");//添加一个空缓存
		
		
		
		//往缓存中放值
		String objkey1="key1",objvalue1="value1";
		cache1.put(new Element(objkey1,objvalue1));//直接放
		
		//遍历取出某个缓存中的所有值
		if(cacheManager.getCache("mycache-one")!=null){
			Cache cache11=cacheManager.getCache("mycache-one");
			if(cache11.getKeys().size()==0){
				System.out.println("mycache-one exits,but no value.");
			}else{
				for(int i=0;i<cache11.getKeys().size();i++){
					Object thekey=cache11.getKeys().get(i);
					Object thevalue=cache11.get(thekey);
					System.out.println("mycache-one-"+i+",key:"+thekey.toString()+",value:"+thevalue.toString());
				}
			}
		}else{
			System.out.println("mycache-one-is null");
		}
		
		/*打印
mycache-one-0,key:key1,value:[ key = key1, value=value1, version=1, hitCount=1, CreationTime = 1366263629054, LastAccessTime = 1366263629054 ]

		 */
		if(cacheManager.getCache("mycache-two")!=null){
			Cache cache2=cacheManager.getCache("mycache-two");
			if(cache2.getKeys().size()==0){
				System.out.println("mycache-two exits,but no value.");
			}else{
				for(int i=0;i<cache2.getKeys().size();i++){
					Object thekey=cache2.getKeys().get(i);
					Object thevalue=cache2.get(thekey);
					System.out.println("mycache-two-"+i+",key:"+thekey.toString()+",value:"+thevalue.toString());
				}
			}
		}else{
			System.out.println("mycache-two-is null");
		}
		/*打印
mycache-two exits,but no value.
		 */
		
		
	}
	
	

}



 

Ehcache在使用的大多数情况,是用ehcache.xml来配置的。在spring中的集成很方便。

下面我们使用ehcache.xml,但不在web环境下,对缓存进行自定义。

org/base/cache/test/myehcache.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd"
         updateCheck="true" monitoring="autodetect"
         dynamicConfig="true">

    
    <diskStore path="java.io.tmpdir"/>
    
    <!-- JTA事务配置。class属性若为空,则默认会按照一个顺序寻找TransactionManager对象。
              也可以自定义,需要实现接口net.sf.ehcache.transaction.manager.TransactionManagerLookup
    -->
    <!--
    <transactionManagerLookup class="net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup"
                              properties="jndiName=java:/TransactionManager" propertySeparator=";"/>
    -->

    <!-- CacheManagerEventListener  缓存监听,根据需要自定义监听类  
    <cacheManagerEventListenerFactory class="" properties=""/>
    -->
    
    <!-- Terracotta服务器集群配置,详细看文档 -->
    <!--
    <terracottaConfig url="localhost:9510"/>
    -->
    
    <defaultCache
           maxEntriesLocalHeap="0"
           eternal="false"
           timeToIdleSeconds="30"
           timeToLiveSeconds="30">
     <!-- <terracotta/>-->
    </defaultCache>

    <!--
           缓存名为myCache1,
    这个缓存最多包含10000个元素在内存中,并将
    闲置超过5分钟和存在超过10分钟的元素释放。
    如果超过10000元素,将溢流到磁盘缓存,并且硬盘缓存最大数量是1000.
           硬盘路径是定义的java.io.tmp。
    -->
    <cache name="myCache1"
           maxEntriesLocalHeap="500"
           maxEntriesLocalDisk="1000"
           eternal="false"
           diskSpoolBufferSizeMB="20"
           timeToIdleSeconds="300"
           timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LFU"
           transactionalMode="off">
        <persistence strategy="localTempSwap"/>
    </cache>


    <!--
            缓存名为sampleCache2。
    此缓存在内存中最大元素的数量是1000。
            没有设置溢出到磁盘,所以1000就是这个缓存的最大值。
            注意,当一个缓存eternal设置成true,那么TimeToLive
    和timeToIdle江不起作用。
    
    <cache name="sampleCache2"
           maxEntriesLocalHeap="1000"
           eternal="true"
           memoryStoreEvictionPolicy="FIFO"/>
-->

    <!-- 
           缓存名为sampleCache3的。
           这个缓存溢出会到磁盘。磁盘缓存存储在虚拟机重新启动前会持久有效。
           磁盘的终止线程的时间间隔设置为3分钟,覆盖默认的2分钟。
     
    <cache name="sampleCache3"
           maxEntriesLocalHeap="500"
           eternal="false"
           overflowToDisk="true"
           diskPersistent="true"
           timeToIdleSeconds="300"
           timeToLiveSeconds="600"
           diskExpiryThreadIntervalSeconds="180"
           memoryStoreEvictionPolicy="LFU">
    </cache>
-->
    <!-- 
    Terracotta集群缓存sampleTerracottaCache。
     
    <cache name="sampleTerracottaCache"
           maxBytesLocalHeap="10m"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="1800">
        <terracotta/>
    </cache>
-->
    

    
</ehcache>

 

 

关于配置的属性的含义,可以到官网的文档中查看,这里给出一些常用的属性。

 

    Cache的以下属性是必须的。

    name:
    cache的唯一标识。

    maxEntriesLocalHeap:
    在内存创建对象的最大数量。0=无限制。
    无限制实际指Integer.MAX_SIZE (2147483647)。

    maxEntriesLocalDisk:
    设置在硬盘上存储的对象的最大数量。默认0,即无限制。

    eternal:
    设置元素是否持久化。如果是,元素不会过期。









    Cache的以下属性是可选的。

    overflowToOffHeap:
    此功能仅在企业版的Ehcache。
    当设置为true,可利用无限制的离堆内存的缓存
    存储,以提高性能。离堆内存是不受Java
    GC限制的。默认值是false。

    maxBytesLocalHeap:
    定义多少字节缓存可能会使用虚拟机的堆。如果一个CacheManager的
    maxBytesLocalHeap已经被定义,这个缓存的指定金额将
    减去从CacheManager的。其他的高速缓存将分享剩下的人。
    此属性的值是<数字> K | K |米| M| G| G
    千字节(K| K),兆字节(M| M)或千兆字节(G| G)。
    例如,maxBytesLocalHeap的“2G”下发2 GB的堆内存。
    如果您指定一个maxBytesLocalHeap,就不能再使用属性maxEntriesLocalHeap。

    maxBytesLocalOffHeap:
    此功能仅在企业版的Ehcache。
    离堆内存量,可以使用这个缓存设置,将保留。
    此设置将设置overflowToOffHeap为true 。设置explicitly为false来禁用溢出行为。
    需要注意的是使用时离堆,设置maxEntriesLocalHeap建议至少100个元素,
    否则性能会出现严重退化,并提出警告。
    可分配的最低金额为128MB。没有最大值。



    maxBytesLocalDisk:
    As for maxBytesLocalHeap, but specifies the limit of disk storage this cache will ever use.

    timeToIdleSeconds:
    设置元素闲置时长。单位:秒。(在eternal设置成false的情况下有效)
    可选属性。值为0意味着元素可以闲置无穷。
    默认值是0。

    timeToLiveSeconds:
    设置元素过期时长。单位:秒。(在eternal设置成false的情况下有效)
    可选属性。值为0意味着,元素可以住无穷。
    默认值是0。

    diskExpiryThreadIntervalSeconds:
    磁盘到期线程运行之间的秒数。默认值为120秒。

    diskSpoolBufferSizeMB:
    这是分配硬盘存储的缓冲区的大小。信息被写入
    这个区域,然后异步写入到磁盘中。默认大小为30MB。
    每个缓冲区仅用于由其缓存。如果你遇到内存溢出错误试着
    降低此值。为了提高硬盘存储性能应考虑增加此值。

    clearOnFlush:
    调用flush()方法时,硬盘存储缓存被清除。
    默认值是true。

    memoryStoreEvictionPolicy:
    内存管理策略,默认是最近最少使用策略(即Least Recently Used,LRU)。
    其他可选的有先进先出策略(即 First In First Out,FIFO),最少使用频率策略
    (即Less Frequently Used,LFU)。

    copyOnRead:
    一个元素被复制时是否从缓存中读取。
    默认false。

    copyOnWrite:
    一个元素被添加到缓存中时是否被复制。
    默认false。



 


EhcacheManagerTest

 

package org.base.cache.test;

import java.net.URL;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.Status;

/**
 * Ehcache缓存管理的初步学习实例
 * @author lushuaiyin
 *
 */
public class EhcacheManagerTest {
	public static net.sf.ehcache.CacheManager cacheManager = null;
	private static String configPath="org/base/cache/test/myehcache.xml";//配置文件路径,一般会放在源文件夹
	
	private static String CACHE_MYCACHE1="myCache1";//定义文件中配置的缓存
	//实例化cacheManager,单例模式
	public static CacheManager getCacheManagerInstance(){
		if (cacheManager == null) {
			URL configUrl=null;
			configUrl = EhcacheManagerTest.class.getClassLoader().getResource(configPath);
			cacheManager = CacheManager.create(configUrl);
		}
		return cacheManager;
	}
	public static net.sf.ehcache.CacheManager getCacheManager() {
		return getCacheManagerInstance();//单例缓存管理
	}
	//这个set可以不开放
	public static void setCacheManager(net.sf.ehcache.CacheManager cacheManager) {
		EhcacheManagerTest.cacheManager = cacheManager;
	}
	
	//添加新缓存
	public static void addCacheByName(String cacheName){
		if(cacheName==null||cacheName.trim().equals("")){
			System.out.println("cacheName is null");
		}else{
			if(getCacheManager().getCache(cacheName.trim())!=null){
				getCacheManager().removeCache(cacheName.trim());
			}
			getCacheManager().addCache(cacheName.trim());
			System.out.println(cacheName+ "重新添加");
		}
	}
	//得到cache对象
	public static Cache getCacheByName(String cacheName){
		Cache cache=null;
		if(cacheName==null||cacheName.trim().equals("")){
			System.out.println("cacheName is null");
		}else{
			if(getCacheManager().getCache(cacheName.trim())!=null){
				cache=getCacheManager().getCache(cacheName.trim());
			}
		}
		
		return cache;
	}
	
	
	//往缓存中添加元素
	public static void putElementToCache(String cacheName,String elementKey,Object elementValue){
		Cache cache=null;
		if(cacheName==null||cacheName.trim().equals("")){
			System.out.println("添加缓存元素失败,cacheName is null");
		}else if(elementKey==null||elementValue==null){
			System.out.println("添加缓存元素失败,elementKey or elementValue is null");
		}else{
			if(getCacheByName(cacheName.trim())!=null){//缓存存在
				cache=getCacheByName(cacheName.trim());
			}else{//缓存不存在
				addCacheByName(cacheName.trim());
				cache=getCacheByName(cacheName.trim());
			}
			//对cache对象添加Element
			Element element=null;
			if(cache.get(elementKey.trim())!=null){
				cache.remove(elementKey.trim());
			}
			element=new Element(elementKey.trim(),elementValue);
			cache.put(element);
			System.out.println("添加缓存元素:"+elementKey+"成功!");
		}
		
	}
	
	//从缓存中获取指定key的值
	public static Object getElementValueFromCache(String cacheName,String elementKey){
		Object result=null;
		Cache cache=null;
		if(cacheName==null||cacheName.trim().equals("")){
			System.out.println("获取缓存元素失败,cacheName is null");
		}else if(elementKey==null){
			System.out.println("获取缓存元素失败,elementKey  is null");
		}else{
			if(getCacheByName(cacheName.trim())!=null){//缓存存在
				cache=getCacheByName(cacheName.trim());
				
				Element element=null;
				if(cache.get(elementKey.trim())!=null){
					element=cache.get(elementKey.trim());
					if(element.getObjectValue()==null){
						System.out.println("缓存中"+elementKey+" 的值为空.");
					}else{
						result=element.getObjectValue();
					}
				}else{
					System.out.println("缓存中"+elementKey+" 的Element值为空.");
				}
			}else{//缓存不存在
				System.out.println("获取缓存元素失败,缓存"+cacheName+" 为空.");
			}
		}
		
		return result;
	}
	
	/**
	 * 把所有cache中的内容删除,但是cache对象还是保留.
	 * Clears the contents of all caches in the CacheManager,
	 *  but without removing any caches.
	 */
	public static void clearAllFromCacheManager(){
		if(getCacheManager()!=null){
			getCacheManager().clearAll();
			System.out.println("CacheManager was clearAll...");
		}
	} 
	
	/**
	 * 把所有cache对象都删除。慎用!
	 * Removes all caches using removeCache(String) for each cache.
	 */
	public static void removalAllFromCacheManager(){
		if(getCacheManager()!=null){
			getCacheManager().removalAll();
			System.out.println("CacheManager was removalAll...");
		}
	} 
	//不用缓存时,要关闭,不然会占用cpu和内存资源。
	public static void shutdownCacheManager(){
		if(getCacheManager()!=null){
			getCacheManager().shutdown();
			System.out.println("CacheManager was shutdown...");
		}
	}
	
	
	//打印方法1,为了测试用
	public static void printCache(Cache cache){
		System.out.println("缓存状态: "+cache.getStatus().toString());
		if(cache==null){
			System.out.println("cache is null,no print info.");
		}else if(cache.getStatus().toString().equals(Status.STATUS_UNINITIALISED)){
			System.out.println("缓存状态: 未初始化"+cache.getStatus().toString());
		}else if(cache.getStatus().toString().equals(Status.STATUS_SHUTDOWN)){
			System.out.println("缓存状态: 已关闭"+cache.getStatus().toString());
		}else if(cache.getStatus().toString().equals(Status.STATUS_ALIVE)){
			if(cache.getKeys().size()==0){
				System.out.println(cache.getName()+" exits,but no value.");
			}else{
				for(int i=0;i<cache.getKeys().size();i++){
					Object thekey=cache.getKeys().get(i);
					Object thevalue=cache.get(thekey);
					System.out.println(cache.getName()+"--"+i+",key:"+thekey.toString()+",value:"+thevalue.toString());
				}
			}
		}
		
		
	}
	
	//打印方法2,为了测试用
	public static void printCacheByName(String cacheName){
		if(cacheName==null||cacheName.trim().equals("")){
			System.out.println("cacheName is null,no print info.");
		}else{
			if(getCacheManager().getCache(cacheName.trim())!=null){
				Cache cache=getCacheManager().getCache(cacheName.trim());
				printCache(cache);
			}else{
				System.out.println(cacheName+" --null");
			}
		}
		
		
	}
	
	public static void main(String[] sdfsf){
		Cache cache1=EhcacheManagerTest.getCacheByName(EhcacheManagerTest.CACHE_MYCACHE1);
		printCache(cache1);
		
		EhcacheManagerTest.putElementToCache(EhcacheManagerTest.CACHE_MYCACHE1, "111", "111haah");
		EhcacheManagerTest.putElementToCache(EhcacheManagerTest.CACHE_MYCACHE1, "222", "222haah");
		EhcacheManagerTest.putElementToCache(EhcacheManagerTest.CACHE_MYCACHE1, "333", "333haah");
		
		printCache(cache1);
		
		EhcacheManagerTest.putElementToCache(EhcacheManagerTest.CACHE_MYCACHE1, "111", "111的新值。");
		
		System.out.println(EhcacheManagerTest.getElementValueFromCache(EhcacheManagerTest.CACHE_MYCACHE1, "111"));
		printCache(cache1);
		
		clearAllFromCacheManager();
		printCache(cache1);
		
		removalAllFromCacheManager();
		printCache(cache1);
		
		shutdownCacheManager();
	}
	/*打印
缓存状态: STATUS_ALIVE
添加缓存元素:111成功!
添加缓存元素:222成功!
添加缓存元素:333成功!
缓存状态: STATUS_ALIVE
添加缓存元素:111成功!
111的新值。
缓存状态: STATUS_ALIVE
CacheManager was clearAll...
缓存状态: STATUS_ALIVE
CacheManager was removalAll...
缓存状态: STATUS_SHUTDOWN
CacheManager was shutdown...

	 */
}



 

通过上面的使用,我们初步了解Ehcache的api。在web环境下,我们可以注入EhcacheManager对象,

把需要的数据,放入缓存。在其他地方取数据的时候,就从过EhcacheManager来获取,而不是直接

从数据库查询。这样就提高了效率。

 

值得注意的是,缓存的使用一般在一些数据比较固定的地方。如果某个查询需要保证数据的实时性,使用缓存

就是错误的做法。

posted @ 2013-05-08 15:36  itank  阅读(1419)  评论(0编辑  收藏  举报