java本地快取技術或框架
阿新 • • 發佈:2019-01-29
java中的本地快取,工作後陸續用到,一直想寫,一直無從下手,最近又涉及到這方面的問題了,梳理了一下。自己構造單例、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 MaplocalCacheStore = new HashMap();
//一個私有的物件,非懶漢模式
private staticSingletonMap singletonMap = newSingletonMap();
//私有構造方法,外部不可以new一個物件
privateSingletonMap(){
}
//靜態方法,外部獲得例項物件
public staticSingletonMap getInstance(){
return singletonMap;
}
//獲得快取中的資料
public ObjectgetValueByKey(String key){
return localCacheStore.get(key);
}
//向快取中新增資料
public voidputValue(String key , Object value){
localCacheStore.put(key, value);
}
}
這種能不能用?可以用,但是非常侷限
但是這種的就是本地快取了嗎?答案顯然不是,為啥呢?
1、 沒有快取大小的設定,無法限定快取體的大小以及儲存資料的限制(max sizelimit);
2、 沒有快取的失效策略(eviction policies);
3、 沒有弱鍵引用,在記憶體佔用吃緊的情況下,JVM是無法回收的(weakrererences 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 = newCacheConfiguration();
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", newDate()));
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 voidmain(String[] args) throws Exception{
GuavaCacheBuilderTest cache = newGuavaCacheBuilderTest();
cache.getNameLoadingCache("bixiao");
}
public voidgetNameLoadingCache(String name) throws Exception{
LoadingCache cache = CacheBuilder.newBuilder()
.maximumSize(20)//設定大小,條目數
.expireAfterWrite(20, TimeUnit.SECONDS)//設定失效時間,建立時間
.expireAfterAccess(20, TimeUnit.HOURS) //設定時效時間,最後一次被訪問
.removalListener(new RemovalListener() { //移除快取的監聽器
public voidonRemoval(RemovalNotification notification) {
System.out.println("有快取資料被移除了");
}})
.build(newCacheLoader(){ //通過回撥載入快取
@Override
public String load(Stringname) 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、需要實時生效資料:啟動時可預熱也可不預熱,但是快取資料變更後,叢集之間需要同步