java中的本地緩存,工做後陸續用到,一直想寫,一直無從下手,最近又涉及到這方面的問題了,梳理了一下。本身構造單例、guava、ehcache基本上涵蓋了目前的大多數行爲了。
java
爲何要有本地緩存?在 系統中,有些數據,數據量小,可是訪問十分頻繁(例如國家標準行政區域數據),針對這種場景,須要將數據搞到應用的本地緩存中,以提高系統的訪問效率,減 少無謂的數據庫訪問(數據庫訪問佔用數據庫鏈接,同時網絡消耗比較大),可是有一點須要注意,就是緩存的佔用空間以及緩存的失效策略。數據庫
所謂的本地混存是相對於網絡而言的(包括集羣,數據庫訪問等)緩存
爲何是本地緩存,而不是分佈式的集羣緩存? 目前的數據,大可能是業務無關的小數據緩存,沒有必要搞分佈式的集羣緩存,目前涉及到訂單和商品的數據,會直接走DB進行請求,再加上分佈式緩存的構建,集羣維護成本比較高,不太適合緊急的業務項目。這裏介紹一下緩存使用的三個階段(摘自info架構師文檔)網絡
本地緩存在那個區域? 目前考慮的是佔用了JVM的heap區域,再細化一點的就是heap中的old區,目前的數據量來看,都是一些小數據,加起來沒有幾百兆,放在heap區 域最快最方便。後期若是須要放置在本地緩存的數據大的時候,能夠考慮在off-heap區域,可是off-heap區域的話,須要考慮對象的序列化(由於 off-heap區域存儲的是二進制的數據),另一個的話就是off-heap的GC問題。其實,若是真的數據量比較大,那其實就能夠考慮搞一個集中式 的緩存系統,能夠是單機,也能夠是集羣,來承擔緩存的做用。架構
搞一個單例模式,裏面有個Map的變量來放置數據很是典型的代碼以下:框架
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); } }
這種能不能用?能夠用,可是很是侷限:分佈式
可是這種的就是本地緩存了嗎?答案顯然不是,爲啥呢? 一、 沒有緩存大小的設置,沒法限定緩存體的大小以及存儲數據的限制(max size limit); 二、 沒有緩存的失效策略(eviction policies); 三、 沒有弱鍵引用,在內存佔用吃緊的狀況下,JVM是沒法回收的(weak rererences keys); 四、 沒有監控統計(statistics); 五、 持久性存儲(persistent store); 因此,這種就直接廢掉了。。。
固然,Cache的配置信息,能夠經過配置文件制定了。。。ide
優勢:功能強大,有失效策略、最大數量設置等,緩存的持久化只有企業版纔有,組件的緩存同步,能夠經過jgroup來實現ui
缺點:功能強大的同時,也使其更加複雜google
引入guava的cacheBuilder來構建緩存
這個很是強大、簡單,經過一個CacheBuilder類就能夠知足需求。
缺點就是若是要組件同步的話,須要本身實現這個功能。
典型的代碼以下:
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、全量預熱,固定的時間段移除全部,而後再全量預熱
適用場景:
一、數據更新不頻繁,例如天天晚上3點更新便可的需求;
二、數據基本沒有變化,例如全國區域性數據;
B、增量預熱(緩存查詢,沒有,則查詢數據庫,有則放入緩存)
適用場景:
一、 數據更新要求緩存中同步更新的場景
集羣內部,緩存的一致性如何保證?
若是採用ehcache的話,可使用框架自己的JGroup來實現組內機器之間的緩存同步。
若是是採用google的cacheBuilder的話,須要本身實現緩存的同步。
A、非實時生效數據:數據的更新不會時時發生,應用啓動的時候更新便可,而後定時程序定時去清理緩存;
B、須要實時生效數據:啓動時可預熱也可不預熱,可是緩存數據變動後,集羣之間須要同步