原文:http://www.cnblogs.com/chsword/archive/2009/04/01/1427627.html
起
最近在做一個(gè)WEB的數(shù)據(jù)統(tǒng)計(jì)的優(yōu)化,但是由于數(shù)據(jù)量大,執(zhí)行一次SQL統(tǒng)計(jì)要比較長的時(shí)間(一般700ms算是正常)。
正常的做法只要加個(gè)緩存就好了。
但是同時(shí)業(yè)務(wù)要求此數(shù)據(jù)最多1分鐘就要更新,而且這一分種內(nèi)數(shù)據(jù)可能會(huì)有較多變化(而且原系統(tǒng)不太易擴(kuò)展)。
也就是說緩存1分鐘就要失效重新統(tǒng)計(jì),而且用戶訪問這頁還很是頻繁,如果使用一般緩存那么用戶體驗(yàn)很差而且很容易造成超時(shí)。
承
點(diǎn)擊看大圖
看到以上需求,第一個(gè)進(jìn)入我大腦的就是從前做游戲時(shí)接觸到的DDraw的雙緩沖顯示方式。
在第一幀顯示的同時(shí),正在計(jì)算第二幀,這樣讀取和計(jì)算就可以分開了,也就避免了讀取時(shí)計(jì)算,提高了用戶體驗(yàn)。
我想當(dāng)然我們也可以將這種方式用于緩存的策略中,但這樣用空間換取時(shí)間的方式還是得權(quán)衡的,因?yàn)椴⒉皇撬袝r(shí)候都值得這么做,但這里我覺得這樣做應(yīng)該是最好的方式了。
注:為了可以好好演示,本篇中的緩存都以IEnumerable的形式來存儲(chǔ),當(dāng)然這個(gè)文中原理也可以應(yīng)用在WebCache中。
這里我使用以下數(shù)據(jù)結(jié)構(gòu)做為存儲(chǔ)單元:
namespace CHCache { /// <summary> /// 緩存介質(zhì) /// </summary> public class Medium { /// <summary> /// 主要存儲(chǔ)介質(zhì) /// </summary> public object Primary { get; set; } /// <summary> /// 次要存儲(chǔ)介質(zhì) /// </summary> public object Secondary { get; set; } /// <summary> /// 是否正在使用主要存儲(chǔ) /// </summary> public bool IsPrimary { get; set; } /// <summary> /// 是否正在更新 /// </summary> public bool IsUpdating { get; set; } /// <summary> /// 是否更新完成 /// </summary> public bool IsUpdated { get; set; } } }
有了這個(gè)數(shù)據(jù)結(jié)構(gòu)我們就可以將數(shù)據(jù)實(shí)現(xiàn)兩份存儲(chǔ)。再利用一些讀寫策略就可以實(shí)現(xiàn)上面我們講的緩存方式。轉(zhuǎn)
整個(gè)的緩存我們使用如下緩存類來控制:
/* * http://www.cnblogs.com/chsword/ * chsword * Date: 2009-3-31 * Time: 17:00 * */ using System; using System.Collections; using System.Collections.Generic; using System.Threading; namespace CHCache { /// <summary> /// 雙存儲(chǔ)的類 /// </summary> public class DictionaryCache : IEnumerable { /// <summary> /// 在此緩存構(gòu)造時(shí)初始化字典對象 /// </summary> public DictionaryCache() { Store = new Dictionary<string, Medium>(); } public void Add(string key,Func<object> func) { if (Store.ContainsKey(key)) {//修改,如果已經(jīng)存在,再次添加時(shí)則采用其它線程 var elem = Store[key]; if (elem.IsUpdating)return; //正在寫入未命中 var th = new ThreadHelper(elem, func);//ThreadHelper將在下文提及,是向其它線程傳參用的 var td = new Thread(th.Doit); td.Start(); } else {//首次添加時(shí)可能也要讀取,所以要本線程執(zhí)行 Console.WriteLine("Begin first write"); Store.Add(key, new Medium {IsPrimary = true, Primary = func()}); Console.WriteLine("End first write"); }
} /// <summary> /// 讀取時(shí)所用的索引 /// </summary> /// <param name="key"></param> /// <returns></returns> public object this[string key] { get { if (!Store.ContainsKey(key))return null; var elem = Store[key]; if (elem.IsUpdated) {//如果其它線程更新完畢,則將主次轉(zhuǎn)置 elem.IsUpdated = false; elem.IsPrimary = !elem.IsPrimary; } var ret = elem.IsPrimary ? elem.Primary : elem.Secondary; var b = elem.IsPrimary ? " from 1" : " form 2"; return ret + b; } } Dictionary<string, Medium> Store { get; set; } public IEnumerator GetEnumerator() { return ((IEnumerable)Store).GetEnumerator(); } }
這里我只實(shí)現(xiàn)了插入一個(gè)緩存,以及讀取的方法。
出處:重典的博客
責(zé)任編輯:bluehearts
上一頁 下一頁 無縫的緩存讀取:雙存儲(chǔ)緩存策略 [2]
◎進(jìn)入論壇網(wǎng)絡(luò)編程版塊參加討論
|