閱讀目錄: 概述最近在看storm,發(fā)現(xiàn)其中的TimeCacheMap算法設(shè)計(jì)頗為高效,就簡(jiǎn)單分享介紹下。 算法介紹TimeCacheMap把要緩存的數(shù)據(jù)分拆存儲(chǔ)到多個(gè)小容器內(nèi),這里稱為桶。另外有個(gè)線程專門在一定時(shí)間內(nèi)去掃描這些桶,一旦發(fā)現(xiàn)過(guò)期后就把整個(gè)桶的數(shù)據(jù)給刪除掉。 其中第二步比較關(guān)鍵,它并不是傳統(tǒng)意義上的去定時(shí)掃描,而是根據(jù)過(guò)期時(shí)間來(lái)觸發(fā),比如如果一個(gè)桶過(guò)期時(shí)間10s,那么這個(gè)線程就10秒觸發(fā)一次把整個(gè)桶刪除即可,當(dāng)然多個(gè)桶的觸發(fā)策略會(huì)有所不同,但思路是同一個(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è)桶存儲(chǔ)起來(lái)。Obj是要鎖的對(duì)象,NumBuckets是桶的數(shù)量,cleaner是清理線程。 為了刪除性能,清理線程會(huì)定期把整個(gè)桶給刪除掉,一般我們會(huì)每次把鏈表中最后一個(gè)桶給清理掉,然后再加入一個(gè)新桶到鏈表頭部。
根據(jù)上面的模擬,調(diào)整到15秒觸發(fā)是一個(gè)比較合理的值,因此推出緩存最長(zhǎng)過(guò)期時(shí)間的公式為: expirationSecs * (1 + 1 / (numBuckets-1)) 如果過(guò)期時(shí)間是30秒,其最長(zhǎng)刪除時(shí)間是: 30*(1+1/(3-1))=30*(1+0.5)=45 因此其過(guò)期時(shí)間范圍即為expirationSecs到expirationSecs * (1 + 1 / (numBuckets-1))之間。 清理線程如上算法的介紹,我們?cè)陬愋偷臉?gòu)造函數(shù)中,實(shí)例化并啟動(dòng)清理線程: 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的立即返回,這需要保證不會(huì)有重復(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í)刪除對(duì)應(yīng)的key,保證不會(huì)有重復(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); } } 刪除對(duì)應(yīng)的key public void Remove(K key) { lock (Obj) { foreach (var item in buckets) { if (item.ContainsKey(key)) item.Remove(key); } } } 總結(jié)在那些年我們一起追過(guò)的緩存寫法(三)中有介紹過(guò)關(guān)于惰性刪除及高效LRU算法優(yōu)化緩存容器的過(guò)期,有興趣的童鞋可以看看。 作者:蘑菇先生出處: http://mushroom.cnblogs.com/
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載。未經(jīng)作者同意下,必須在文章頁(yè)面明顯標(biāo)出原文鏈接及作者,否則保留追究法律責(zé)任的權(quán)利。 如果您認(rèn)為這篇文章還不錯(cuò)或者有所收獲,可以點(diǎn)擊右下角的【推薦】按鈕,因?yàn)槟愕闹С质俏依^續(xù)寫作,分享的最大動(dòng)力! |
|
來(lái)自: 昵稱10504424 > 《工作》