我這裡說的cache不是指CPU和RAM之間的緩衝,而是java應用中間常用的緩衝。最常使用的場合就是訪問資料庫的時候為了提高效率而使用的
cache。一般的用法就是把資料從資料庫讀到記憶體,然後之後的資料訪問都從記憶體來讀,從而減少對資料庫的讀取次數來提高效率。
在使用cache的時候最容易犯的錯誤就是cache涉及了商務邏輯。使用cache的原意是只是提高程式效率,而不應該幹涉程式結果。按照cahce的定義,cache應該是對資料訪問端透明
地工作。所以在使用cache的時候我們可以問一下自己:“我把cache拿掉後程式還能運行嗎?”
“cache拿掉前後程式啟動並執行結果一直嗎?”。如果答案是否,那您就得重新考慮您的cache方案。我自己就碰到過這樣的bug:資料庫的有個表裡面都
是些配置資訊,也就是說是些
讀訪問遠大於寫訪問
的資料。然後這些資料被理所應當地在程式裡面做成記憶體
cache。問題是有個delete方法刪除了一條資料,但是沒有更新記憶體cache。所以讀操作的客戶代碼還是能讀到這條資料。問題的根本就是後台資料和cache不一致。
cache的容量一般相對後台資料量都比較有限。一旦cache滿了就勢必要選擇最沒用的資料從cache裡面刪除掉,為新資料騰出空間。這裡就涉及
cahce演算法cache algorithm或者叫替換演算法。在java的cache產品中一般叫evict
policy。下面我們來看一下常用的cache algorithm。
- 最近最少使用演算法 Least Recently Used (LRU):
這個演算法就是把最近一次使用時間離現在時間最遠的資料刪除掉。最直觀的結構應該是List,採取的演算法是:每次訪問一個元素後把這個元素放在
List一端,這樣一來最遠使用的元素自然就被放到List的另一端。每次evict的時候就把那最遠使用的元素remove掉。但是現實中常採用的資料
結構是HashMap +
List。因為List太慢,List只能提供O(n)的演算法,要使得它的add,remove和get的演算法為O(1)就必須使用HashMap。最簡
單的實現就是利用JDK內建的LinkedHashMap,你可以把它看作普通的HashMap之外,每個元素的key都用鏈表串連起來從而實現順序結
構。LinkedHashMap預設的元素順序是put的順序,但是如果使用帶參數的建構函式,那麼LinkedHashMap會根據訪問順序來調整內部
順序。
LinkedHashMap的get()方法除了返回元素之外還可以把被訪問的元素放到鏈表的底端,這樣一來每次頂端的元素就是remove的元素。
這個比較直觀,就是個Queue。但是還是為了保證O(1)的效率,還是要用LinkedHashMap。但是這次使用預設的無參數的建構函式,LinkedHashMap內部使用的是put的順序。因此每次remove頂端即可。
- 最近最多時用演算法Most Recently Used (MRU)
這個演算法和LRU是相反操作,所以沒什麼新鮮的東西。每次remove LinkedHashMap底端的元素就可以實現。
- 使用次數最小演算法 Least Frequently Used (LFU)
這
個演算法的核心是每次訪問元素的時候,這個元素的次數屬性加1。所以每次remove操作就是次數屬性最小的元素。這次沒法用LinkedHashMap來
實現了,因為LinkedHashMap沒有接受comparator參數的功能。有些程式是用LinkedList +
HashMap來實現。這樣add和get操作還是O(1),只是remove操作的時候先要排序然後再remove,最快也就是O(n*log n),譬如利用快速排序。或者乾脆在remove的時候只是做尋找最小元素的演算法來除去訪問次數最小的元素。
另外還有其他的cache演算法,譬如按照元素內建的到期值expiration和隨機random來evict元素的演算法。在真正的cache產品中資料結構和演算法要比上面描述的要複雜。有些產品自己定義一些資料結構來提高效率,畢竟cache是為了提高效率而產生的。進階的cache產品還可能包括事務機制,JMX和支援cluster環境這樣複雜的特性。
目前比較主流的cache產品有EHCache,OSCache,SwarmCache和JBoss Cache,很多使用Hibernate的人都對都此有些瞭解。關於JBoss Cache,它在將來可能被JBoss的另外一個叫infinispan
的資料格平台項目所替代。
作者: 盧聲遠 <michaellufhl@yahoo.com.cn>
參考:
1: http://en.wikipedia.org/wiki/Cache_algorithms
2: http://docs.jboss.org/jbosscache/2.0.0.GA/JBossCache-UserGuide/en/html_single/