閱讀目錄: 概述最近在看storm,發(fā)現(xiàn)其中的TimeCacheMap算法設(shè)計(jì)頗為高效,就簡單分享介紹下。 算法介紹TimeCacheMap把要緩存的數(shù)據(jù)分拆存儲到多個(gè)小容器內(nèi),這里稱為桶。另外有個(gè)線程專門在一定時(shí)間內(nèi)去掃描這些桶,一旦發(fā)現(xiàn)過期后就把整個(gè)桶的數(shù)據(jù)給刪除掉。 其中第二步比較關(guān)鍵,它并不是傳統(tǒng)意義上的去定時(shí)掃描,而是根據(jù)過期時(shí)間來觸發(fā),比如如果一個(gè)桶過期時(shí)間10s,那么這個(gè)線程就10秒觸發(fā)一次把整個(gè)桶刪除即可,當(dāng)然多個(gè)桶的觸發(fā)策略會有所不同,但思路是同一個(gè)。 private LinkedList<Dictionary<K, V>> buckets; private readonly object Obj = new object(); private static readonly int NumBuckets = 3; private Thread cleaner; 上面使用了k、v的形式作為緩存數(shù)據(jù)結(jié)構(gòu),每個(gè)Dictionary是一個(gè)桶,然后使用鏈表把多個(gè)桶存儲起來。Obj是要鎖的對象,NumBuckets是桶的數(shù)量,cleaner是清理線程。 為了刪除性能,清理線程會定期把整個(gè)桶給刪除掉,一般我們會每次把鏈表中最后一個(gè)桶給清理掉,然后再加入一個(gè)新桶到鏈表頭部。
根據(jù)上面的模擬,調(diào)整到15秒觸發(fā)是一個(gè)比較合理的值,因此推出緩存最長過期時(shí)間的公式為: expirationSecs * (1 + 1 / (numBuckets-1)) 如果過期時(shí)間是30秒,其最長刪除時(shí)間是: 30*(1+1/(3-1))=30*(1+0.5)=45 因此其過期時(shí)間范圍即為expirationSecs到expirationSecs * (1 + 1 / (numBuckets-1))之間。 清理線程如上算法的介紹,我們在類型的構(gòu)造函數(shù)中,實(shí)例化并啟動清理線程: public TimeCacheMap(int expirationSecs, int numBuckets, ExpiredCallBack ex) { if (numBuckets < 2) throw new ArgumentException("numBuckets must be >=2"); this.buckets = new LinkedList<Dictionary<K, V>>(); for (int i = 0; i < numBuckets; i++) buckets.AddFirst(new Dictionary<K, V>()); var expirationMillis = expirationSecs * 1000; var sleepTime = expirationMillis / (numBuckets - 1); cleaner = new Thread(() => { while (true) { Dictionary<K, V> dead = null; Thread.Sleep(sleepTime); lock (Obj) { dead = buckets.Last(); buckets.RemoveLast(); buckets.AddFirst(new Dictionary<K, V>()); } if (ex != null) ex(dead); } }); cleaner.IsBackground = true; cleaner.Start(); } 代碼執(zhí)行步驟:
整個(gè)桶的數(shù)據(jù)刪除只需要加一次鎖即可,保證其高效。 獲取、插入、刪除遍歷整個(gè)鏈表,查詢到第一個(gè)滿足key的立即返回,這需要保證不會有重復(fù)key。 public V Get(K key) { lock (Obj) { foreach (var item in buckets) { if (item.ContainsKey(key)) return item[key]; } return default(V); } } 在插入時(shí)刪除對應(yīng)的key,保證不會有重復(fù)的key出現(xiàn)。 public void Put(K key, V value) { lock (Obj) { foreach (var item in buckets) { item.Remove(key); } buckets.First().Add(key, value); } } 刪除對應(yīng)的key public void Remove(K key) { lock (Obj) { foreach (var item in buckets) { if (item.ContainsKey(key)) item.Remove(key); } } } 總結(jié)在那些年我們一起追過的緩存寫法(三)中有介紹過關(guān)于惰性刪除及高效LRU算法優(yōu)化緩存容器的過期,有興趣的童鞋可以看看。 作者:蘑菇先生出處: http://mushroom.cnblogs.com/
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載。未經(jīng)作者同意下,必須在文章頁面明顯標(biāo)出原文鏈接及作者,否則保留追究法律責(zé)任的權(quán)利。 如果您認(rèn)為這篇文章還不錯(cuò)或者有所收獲,可以點(diǎn)擊右下角的【推薦】按鈕,因?yàn)槟愕闹С质俏依^續(xù)寫作,分享的最大動力! |
|