在開發(fā)中大型Java軟件項(xiàng)目時(shí),對(duì)于頻繁讀寫數(shù)據(jù)庫的操作,為了減輕數(shù)據(jù)庫的壓力,我們常常會(huì)用到緩存。因?yàn)閿?shù)據(jù)庫連接是非?!鞍嘿F”的資源,因此我們需要增加一個(gè)抽象層來解決,緩存層應(yīng)用而生。
緩存的數(shù)據(jù)是保存在內(nèi)存中的,而內(nèi)存的速度是硬盤的10萬倍,所以讀取速度相當(dāng)快。第一次訪問從數(shù)據(jù)庫讀取數(shù)據(jù), 并且放到緩存中;后續(xù)訪問直接從緩存中讀取數(shù)據(jù);發(fā)生變化,既要更新數(shù)據(jù)庫, 也要更新緩存。
說到緩存,大家可能直接印象就是Redis,方便好用。但是Redis是通過網(wǎng)絡(luò)傳輸?shù)模援?dāng)數(shù)據(jù)庫大的時(shí)候Redis的壓力就太大了,這時(shí)候我們就需要在本地內(nèi)存中建立緩存。本地緩存最簡(jiǎn)單的方式是寫一個(gè)HashMap,以key-value的形式實(shí)現(xiàn)。但是這種方式不能對(duì)緩存的有效期進(jìn)行設(shè)置,如果不手動(dòng)將map置為null,將會(huì)一直占用內(nèi)存空間。所以需要一個(gè)緩存框架來解決問題。
本地的緩存框架有很多,常用的有Ehcache,Cacheonix,ASimpleCache ,JBoss Cache,Voldemort 。今天來說EHCache。
-
Ehcache是一個(gè)Java實(shí)現(xiàn)的開源分布式緩存框架,EhCache 可以有效地減輕數(shù)據(jù)庫的負(fù)載,可以讓數(shù)據(jù)保存在不同服務(wù)器的內(nèi)存中,在需要數(shù)據(jù)的時(shí)候可以快速存取。同時(shí)EhCache 擴(kuò)展非常簡(jiǎn)單,官方提供的Cache配置方式有好幾種??梢酝ㄟ^聲明配置、在xml中配置、在程序里配置或者調(diào)用構(gòu)造方法時(shí)傳入不同的參數(shù)。
ehcache架構(gòu).png Ehcache有以下特點(diǎn):
- 存取速度非???,性能很不錯(cuò)。
- 可以應(yīng)用多種緩存策略。
- 分級(jí)緩存,用戶可以指定哪些數(shù)據(jù)在硬盤中緩存,哪些數(shù)據(jù)在內(nèi)存中緩存。
- 可以通過RMI、可插入API等方式進(jìn)行分布式緩存。
- 具有緩存和緩存管理器的偵聽接口。
- 支持多緩存管理器實(shí)例,以及一個(gè)實(shí)例的多個(gè)緩存區(qū)域。
- 默認(rèn)提供Hibernate的緩存實(shí)現(xiàn)。
配置有好多方式,但是萬變不離其宗,知道配置參數(shù)意義才是最重要的。
- 在使用EHCache時(shí),需要在工程根目錄里配置ehcache.xml,如果想通過自己手動(dòng)控制緩存添加和釋放就像redis那樣操作,可以寫一個(gè)EHCacheUtil類來操作。
ehcache.xml配置如下:
<ehcache>
<!--<diskStore path="java.io.tmpdir"/> -->
<diskStore path="data/ehcache" />
<defaultCache maxElementsInMemory="10000" eternal="false"
overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120"
diskPersistent="false" diskExpiryThreadIntervalSeconds="120" />
<cache name="tagCache" maxElementsInMemory="10000" eternal="false"
timeToIdleSeconds="300" timeToLiveSeconds="1800" overflowToDisk="true"
diskPersistent="false" />
</ehcache>
配置的一些參數(shù)含義如下:
maxElementsInMemory:設(shè)置緩存中允許存放的最大條目數(shù)量
eternal:緩存內(nèi)容是否永久存儲(chǔ)在內(nèi)存;該值設(shè)置為true時(shí),timeToIdleSeconds和timeToLiveSeconds兩個(gè)屬性的值就不起作用了。
overflowToDisk:如果內(nèi)存中的數(shù)據(jù)超過maxElementsInMemory,是否使用磁盤存儲(chǔ)。
timeToLiveSeconds:緩存自創(chuàng)建日期起至失效時(shí)的間隔時(shí)間;
timeToIdleSeconds:緩存創(chuàng)建以后,最后一次訪問緩存的日期至失效之時(shí)的時(shí)間間隔;
如果僅有timeToLiveSeconds那么自創(chuàng)建時(shí)間開始 間隔x后緩存失效;
如果沒有timeToLiveSeconds那么自最后一次訪問緩存 間隔y后 緩存失效;
如果既有timeToLiveSeconds也有timeToIdleSeconds那么取最小數(shù)算作間隔時(shí)間;min(x,y);
diskPersistent:磁盤存儲(chǔ)的條目是否永久保存
diskExpiryThreadIntervalSeconds:磁盤清理線程的運(yùn)行時(shí)間間隔
注意:時(shí)間都是以秒為單位
配置完ehcache.xml之后,就可以寫一個(gè)EHCacheUtil類了:
/**
* 對(duì)EHCache進(jìn)行了簡(jiǎn)單的封裝
* 建議在頻繁使用且重負(fù)載的函數(shù)實(shí)現(xiàn)中使用緩存
* Ehcache會(huì)將每個(gè)緩存配置的文件路徑下創(chuàng)建一個(gè)cache_name.data文件,如果使用的磁盤持久化技術(shù),還會(huì)生成一個(gè)cache name.index文件。
* @author kouyy
*/
public class EHCacheUtil {
static CacheManager manager=null;
static String configfile="ehcache.xml";
//EHCache初始化
static{
try {
manager = CacheManager.create(EHCacheUtil.class.getClassLoader().getResourceAsStream(configfile));
} catch (CacheException e) {
e.printStackTrace();
}
}
/**
* 將數(shù)據(jù)存入Cache
* @param cachename Cache名稱
* @param key 類似redis的Key
* @param value 類似redis的value,value可以是任何對(duì)象、數(shù)據(jù)類型,比如person,map,list等
*/
public static void put(String cachename,Serializable key,Serializable value){
manager.getCache(cachename).put(new Element(key, value));
}
/**
* 獲取緩存cachename中key對(duì)應(yīng)的value
* @param cachename
* @param key
* @return
*/
public static Serializable get(String cachename,Serializable key){
try {
Element e=manager.getCache(cachename).get(key);
if(e==null){
return null;
}
return e.getValue();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (CacheException e) {
e.printStackTrace();
}
return null;
}
/**
* 清除緩存名稱為cachename的緩存
* @param cachename
*/
public static void clearCache(String cachename){
try {
manager.getCache(cachename).removeAll();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
/**
* 移除緩存cachename中key對(duì)應(yīng)的value
* @param cachename
* @param key
*/
public static void remove(String cachename,Serializable key){
manager.getCache(cachename).remove(key);
}
}
實(shí)際操作很像redis,都是對(duì)key-value的操作,只是ehcache在每次操作的時(shí)候需要指明緩存的名稱,僅此而已。
在同類的Java緩存框架中,Ehcache配置相對(duì)簡(jiǎn)單,也比較容易上手,最大的優(yōu)勢(shì)是它支持分布式緩存。
