在看hibernate的官方文檔時,看到關於緩衝的介紹。
hibernate在緩衝管理上做的很好,具體使用,本章不做講解,本篇主要研究EhCache的用法。其中hibernate使用到的緩衝供應商列表如下:
| Cache |
Provider class |
Type |
Cluster Safe |
Query 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設定檔,那麼必須用代碼配置一個defaultCacheConfigurationCacheConfiguration defaultCacheConfiguration=new CacheConfiguration();defaultCacheConfiguration.setMaxEntriesLocalHeap(0);defaultCacheConfiguration.setEternal(false);defaultCacheConfiguration.setTimeToIdleSeconds(30);defaultCacheConfiguration.setTimeToLiveSeconds(30);config.addDefaultCache(defaultCacheConfiguration);//設定預設cachenet.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 KB(K| K),MB(M| M)或千MB(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對象添加ElementElement 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_ALIVECacheManager was clearAll...緩衝狀態: STATUS_ALIVECacheManager was removalAll...緩衝狀態: STATUS_SHUTDOWNCacheManager was shutdown... */}
通過上面的使用,我們初步瞭解Ehcache的api。在web環境下,我們可以注入EhcacheManager對象,
把需要的資料,放入緩衝。在其他地方取資料的時候,就從過EhcacheManager來擷取,而不是直接
從資料庫查詢。這樣就提高了效率。
值得注意的是,緩衝的使用一般在一些資料比較固定的地方。如果某個查詢需要保證資料的即時性,使用緩衝
就是錯誤的做法。