在java應用中,對於訪問頻率比較高,又不怎么變化的數據,常用的解決方案是把這些數據加入緩存。相比DB,緩存的讀取效率快好不少。java應用緩存一般分兩種,一是進程內緩存,就是使用java應用虛擬機內存的緩存;另一個是進程外緩存,現在我們常用的各種分布式緩存。相比較而言,進程內緩存比進程外緩存快很多,而且編碼也簡單;但是,進程內緩存的存儲量有限,使用的是java應用虛擬機的內存,而且每個應用都要存儲一份,有一定的資源浪費。進程外緩存相比進程內緩存,會慢些,但是,存儲空間可以橫向擴展,不受限制。
這里是幾中場景的訪問時間
-------------------------------------------------------------------
| 從數據庫中讀取一條數據(有索引) | 十幾毫秒 |
| 從遠程分布式緩存讀取一條數據 | 0.5毫秒 |
| 從內存中讀取1MB數據 | 十幾微妙 |
-------------------------------------------------------------------
進程內緩存和進程外緩存,各有優缺點,針對不同場景,可以分別采用不同的緩存方案。對於數據量不大的,我們可以采用進程內緩存。或者只要內存足夠富裕,都可以采用,但是不要盲目以為自己富裕,不然可能會導致系統內存不夠。
下面要分享的是一個代碼級別的,對進程內緩存的經驗總結。面向jdk1.8版本。
在有效時間內緩存單個對象
public class LiveCache<T> { // 緩存時間 private final int cacheMillis; // 緩存對象 private final T element; // 緩存對象創建時間 private final long createTime; public LiveCache(int cacheMillis, T element) { this.cacheMillis = cacheMillis; this.element = element; this.createTime = System.currentTimeMillis(); } // 獲取緩存對象 public T getElement() { long currentTime = System.currentTimeMillis(); if(cacheMillis > 0 && currentTime - createTime > cacheMillis) { return null; } else { return element; } } // 獲取緩存對象,忽略緩存時間有效性 public T getElementIfNecessary() { return element; } } public static void main(String[] args) { int cacheMilis = 1000 ; LiveCache<Object> liveCache = new LiveCache<>(cacheMilis, new Object()) ; liveCache.getElement() ; liveCache.getElementIfNecessary() ; }
有效時間內,緩存單個對象,可異步刷新
@FunctionalInterface public interface LiveFetch<T> { // 刷新緩存接口 T fetch() ; } public class LiveManager<T> { // 緩存時間 private int cacheMillis; // 緩存對象 private LiveCache<T> liveCache; // 刷新緩存的對象 private LiveFetch<T> liveFetch ; private Logger logger = LoggerFactory.getLogger(LiveManager.class) ; // 刷新緩存開關 private boolean refresh = false ; public LiveManager(int cacheMillis, LiveFetch<T> liveFetch) { this.cacheMillis = cacheMillis ; this.liveFetch = liveFetch ; } /** * fetch cache ; if cache expired , synchronous fetch * @return */ public T getCache() { initLiveCache(); if(liveCache != null) { T t ; if((t= liveCache.getElement()) != null) { return t ; } else { t = liveFetch.fetch() ; if(t != null) { liveCache = new LiveCache<T>(cacheMillis, t) ; return t ; } } } return null ; } /** * fetch cache ; if cache expired , return old cache and asynchronous fetch * @return */ public T getCacheIfNecessary() { initLiveCache(); if(liveCache != null) { T t ; if((t= liveCache.getElement()) != null) { return t ; } else { refreshCache() ; return liveCache.getElementIfNecessary() ; } } return null ; } /** * init liveCache */ private void initLiveCache() { if(liveCache == null) { T t = liveFetch.fetch() ; if(t != null) { liveCache = new LiveCache<T>(cacheMillis, t) ; } } } /** * asynchronous refresh cache */ private void refreshCache() { if(refresh) return ; refresh = true ; try { Thread thread = new Thread(() -> { try { T t = liveFetch.fetch(); if (t != null) { liveCache = new LiveCache<>(cacheMillis, t); } } catch (Exception e){ logger.error("LiveManager.refreshCache thread error.", e); } finally { refresh = false ; } }) ; thread.start(); } catch (Exception e) { logger.error("LiveManager.refreshCache error.", e); } } } public class Test { public static void main(String[] args) { int cacheMilis = 1000 ; LiveManager<Object> liveManager = new LiveManager<>(cacheMilis,() -> new Test().t1()) ; liveManager.getCache() ; liveManager.getCacheIfNecessary() ; } public Object t1(){ return new Object() ; } }
有效緩存內,緩存多個對象,map結構存儲,可異步刷新
@FunctionalInterface public interface LiveMapFetch<T> { // 異步刷新數據 T fetch(String key) ; } public class LiveMapManager<T> { private int cacheMillis; private Map<String,LiveCache<T>> liveCacheMap; private LiveMapFetch<T> liveMapFetch; private Logger logger = LoggerFactory.getLogger(LiveMapManager.class) ; private boolean refresh = false ; public LiveMapManager(int cacheMillis, LiveMapFetch<T> liveMapFetch) { this.cacheMillis = cacheMillis ; this.liveMapFetch = liveMapFetch ; } /** * fetch cache ; if cache expired , synchronous fetch * @return */ public T getCache(String key) { initLiveCache(); T t ; if(liveCacheMap.containsKey(key) && (t = liveCacheMap.get(key).getElement()) != null) { return t ; } else { t = liveMapFetch.fetch(key) ; if(t != null) { LiveCache<T> liveAccess = new LiveCache<T>(cacheMillis, t) ; liveCacheMap.put(key, liveAccess) ; return t ; } } return null ; } /** * fetch cache ; if cache expired , return old cache and asynchronous fetch * @return */ public T getCacheIfNecessary(String key) { initLiveCache(); T t ; if(liveCacheMap.containsKey(key) && (t = liveCacheMap.get(key).getElement()) != null) { return t ; } else { if(liveCacheMap.containsKey(key)) { refreshCache(key) ; return liveCacheMap.get(key).getElementIfNecessary() ; } else { t = liveMapFetch.fetch(key) ; if(t != null) { LiveCache<T> liveAccess = new LiveCache<T>(cacheMillis, t) ; liveCacheMap.put(key, liveAccess) ; return t ; } } } return t ; } /** * init liveCache */ private void initLiveCache() { if(liveCacheMap == null) { liveCacheMap = new HashMap<>() ; } } /** * asynchronous refresh cache */ private void refreshCache(String key) { if(refresh) return ; refresh = true ; try { Thread thread = new Thread(() -> { try { T t = liveMapFetch.fetch(key); if (t != null) { LiveCache<T> liveAccess = new LiveCache<>(cacheMillis, t); liveCacheMap.put(key, liveAccess); } } catch (Exception e) { logger.error("LiveMapManager.refreshCache thread error.key:",e); } finally { refresh = false ; } }) ; thread.start(); } catch (Exception e) { logger.error("LiveMapManager.refreshCache error.key:" + key, e); } } } public class Test { public static void main(String[] args) { int cacheMilis = 1000 ; LiveMapManager<Object> liveManager = new LiveMapManager<>(cacheMilis,(String key) -> new Test().t1(key)) ; liveManager.getCache("key") ; liveManager.getCacheIfNecessary("key") ; } public Object t1(String key){ return new Object() ; } }
resource : https://github.com/jxlzl1988/MyCache
