我這里說的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的元素。
另外還有其他的cache算法,譬如按照元素自帶的過期值expiration和隨機random來evict元素的算法。在真正的cache產品中數據結構和算法要比上面描述的要復雜。有些產品自己定義一些數據結構來提高效率,畢竟cache是為了提高效率而產生的。高級的cache產品還可能包括事務機制,JMX和支持cluster環境這樣復雜的特性。
- First In, First Out算法
- 最近最多時用算法Most Recently Used (MRU)
- 使用次數最小算法 Least Frequently Used (LFU)
另外還有其他的cache算法,譬如按照元素自帶的過期值expiration和隨機random來evict元素的算法。在真正的cache產品中數據結構和算法要比上面描述的要復雜。有些產品自己定義一些數據結構來提高效率,畢竟cache是為了提高效率而產生的。高級的cache產品還可能包括事務機制,JMX和支持cluster環境這樣復雜的特性。
目前比較主流的cache產品有EHCache,OSCache,SwarmCache和JBoss Cache,很多使用Hibernate的人都對都此有些了解。關於JBoss Cache,它在將來可能被JBoss的另外一個叫
infinispan 的數據網格平台項目所替代。