新聞中心
我有話說

創(chuàng)新互聯(lián)公司專注于企業(yè)營銷型網(wǎng)站、網(wǎng)站重做改版、隆德網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、HTML5、成都商城網(wǎng)站開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為隆德等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
本來這一篇我打算放到后面再說,可是之前泄漏了一點(diǎn)關(guān)于緩存決策的代碼后被好多人催更了。
在此感謝大家的支持,讓我更有動(dòng)力的寫這個(gè)系列。你們的關(guān)注讓我覺得我的決定是對(duì)的,我會(huì)堅(jiān)持下去把這個(gè)項(xiàng)目做完。
另外非常感謝老虎,在百忙之中給我們趕出需求文檔,當(dāng)我們?cè)谙硎苤苣┑臅r(shí)候他還在公司加班,即便這樣,他依然為我們的開源項(xiàng)目奉獻(xiàn)著。
此時(shí)我不知道該說些什么,只能以我的行動(dòng)來回報(bào)大家,廢話不多說了,入正題。
緩存決策
先澄清下,這個(gè)名字是我杜撰的,因?yàn)槲矣X得在我的項(xiàng)目中它起到了這樣的作用。
緩存:在我做的這個(gè)功能中涉及到內(nèi)存和redis兩部分的緩存。
決策:我從百度找的翻譯,指做出決定或選擇,是一種“在各種替代方案中考慮各項(xiàng)因素作出選擇”的認(rèn)知、思考過程。
那么緩存決策到底是干什么的?
說白了就是選擇使用數(shù)據(jù)庫還是緩存。
如何適合緩存決策
緩存決策的由來 - 我是懶人
因?yàn)槲覒?,所以我要想辦法偷懶。
我希望有一個(gè)類庫可以幫助我來判斷當(dāng)前的數(shù)據(jù)是到緩存里取,還是數(shù)據(jù)庫里取。
而為了實(shí)現(xiàn)這樣的一個(gè)功能,我覺得我應(yīng)該建立一個(gè)規(guī)則,這個(gè)規(guī)則來幫助我判斷當(dāng)前數(shù)據(jù)在緩存里是不是有一份拷貝。
我對(duì)緩存的判斷規(guī)則有什么要求?
就以目前項(xiàng)目來說,我緩存是整表緩存的,所以我需要判斷的是當(dāng)前數(shù)據(jù)是屬于哪個(gè)表。
既然如此,那我判斷的依據(jù)應(yīng)該是這樣: 緩存決策規(guī)則.表名列表.包含(數(shù)據(jù).表名) == true
只要滿足上面的條件,說明當(dāng)前數(shù)據(jù)在緩存里是有拷貝的。
如何管理這些判斷規(guī)則?
繼續(xù)上面提到的包含,我們?cè)俜治鲆幌?,包含的判斷依?jù)其實(shí)是逐一比對(duì)相等,所以我想了個(gè)類名:EqualsMonitorManager,這里的Monitor是監(jiān)視器的意思,后面的類都會(huì)跟這個(gè)詞有關(guān)。
這個(gè)類有4個(gè)基本的方法:Add、Remove、Get、IsMonitoring ,看起來其實(shí)是很像字典的對(duì)吧?其實(shí)內(nèi)部實(shí)現(xiàn)確實(shí)依賴了字典,對(duì)字典做了一些封裝。
為了方面以后擴(kuò)展支持到更多場(chǎng)景而不局限于緩存,我定義的時(shí)候使用到了泛型。
- public static partial class EqualsMonitorManager
- where TValue : IEquatable
- {
- private static class MonitorCaller
- {
- public static Action
Add; - public static Action
Remove; - public static Func
, TValue> Get; - public static Func
IsMonitoring; - }
- #region Members
- private static Dictionary
> _dicStringMonitor = new Dictionary >(); - #endregion
- static EqualsMonitorManager()
- {
- StringMonitorCallerInit();
- }
- private static void StringMonitorCallerInit()
- {
- MonitorCaller
.Add = (string key, TValue value) => - {
- if (!_dicStringMonitor.ContainsKey(key))
- {
- _dicStringMonitor.Add(key, new List
()); - }
- _dicStringMonitor[key].Add(value);
- };
- MonitorCaller
.Remove = (string key) => - {
- if (_dicStringMonitor.ContainsKey(key))
- _dicStringMonitor.Remove(key);
- };
- MonitorCaller
.Get = (string key, Func predicate) => - {
- if (_dicStringMonitor.ContainsKey(key))
- return _dicStringMonitor[key].FirstOrDefault(predicate);
- else
- return default(TValue);
- };
- MonitorCaller
.IsMonitoring = (string key, TValue value) => - {
- if (!_dicStringMonitor.ContainsKey(key))
- {
- return false;
- }
- return _dicStringMonitor[key].Exists(x => x.Equals(value));
- };
- }
- }
- public static partial class EqualsMonitorManager
- {
- public static void Add(TKey key, TValue value)
- {
- if (key == null)
- {
- throw new ArgumentNullException();
- }
- MonitorCaller
.Add(key, value); - }
- public static void Remove(TKey key)
- {
- if (key == null)
- {
- throw new ArgumentNullException();
- }
- MonitorCaller
.Remove(key); - }
- public static TValue Get(TKey key, Func
predicate) - {
- if (key == null)
- {
- throw new ArgumentNullException();
- }
- return MonitorCaller
.Get(key, predicate); - }
- public static bool IsMonitoring(TKey key, TValue value)
- {
- if (key == null)
- {
- throw new ArgumentNullException();
- }
- return MonitorCaller
.IsMonitoring(key, value); - }
- }
這里的代碼用到了老趙博客中的一篇關(guān)于“逆泛型”的代碼,這里是未經(jīng)優(yōu)化的,寫的倉促。
這里我就不多解釋為什么會(huì)這么寫這個(gè)類了,有興趣可以去翻老趙的博客,寫的很詳細(xì),對(duì)于初學(xué)者來說這里有點(diǎn)繞,建議可以去看看。
這里只是創(chuàng)建了一個(gè)最基礎(chǔ)的封裝過的“字典”,用于管理判斷規(guī)則。
初始化判斷規(guī)則
有了管理規(guī)則的類,那么我們的項(xiàng)目中首先要做的就是初始化這些規(guī)則,否則沒有規(guī)則后面的寫下去也用不了。
細(xì)心的朋友可能會(huì)發(fā)現(xiàn),EqualsMonitorManager的TValue需要繼承自IEquatable接口,因?yàn)閮?nèi)部判斷相等是用了這個(gè)接口的Equals方法。
那么,我們第一個(gè)緩存決策類出現(xiàn)了,它就是RedisCacheMonitor。
- public class RedisCacheMonitor : IEquatable
- {
- public string Key { get { return MonitorConstant.REDIS_KEY; } }
- public string TableName { get; set; }
- public string[] Fields { get; set; }
- #region IEquatable
成員 - public bool Equals(RedisCacheMonitor other)
- {
- if (other == null)
- {
- return false;
- }
- return this.TableName == other.TableName;
- }
- #endregion
- }
我們可以發(fā)現(xiàn),這個(gè)類的自由度很大,唯一的約束就是要實(shí)現(xiàn)IEquatable接口,這樣EqualMonitorManager的可擴(kuò)展性就充分被利用了起來。
而RedisCacheMonitor就可以任由我們來發(fā)揮,我們只需要告訴EqualMonitorManager如何去判斷相等即可。
TableName表示緩存的表名,F(xiàn)ields是使用了Redis HGet命令的一個(gè)參數(shù)名,表示哪些字段可以作為關(guān)鍵字來查詢數(shù)據(jù)或者說需要緩存哪些字段為關(guān)鍵字。
接下來就是如何把一個(gè)RedisCacheMonitor加入到EqualMonitorManager
- var monitor = new RedisCacheMonitor() { TableName = "User", Fields = new string[] { "Id", "UserName" } };
- EqualsMonitorManager
.Add(monitor.Key, monitor);
是的,就這么簡單,我們的緩存規(guī)則就加完了。剩下就是操作Redis,把User表緩存起來我就不多說了。
#p#
自動(dòng)緩存決策與手動(dòng)緩存決策
為什么會(huì)有自動(dòng)和手動(dòng)兩種?
因?yàn)槲也僮鲾?shù)據(jù)庫用的EF,查詢條件是表達(dá)式樹,為了降低解析表達(dá)式樹的工作量暫時(shí)選擇了自動(dòng)和手動(dòng)。
如何實(shí)現(xiàn)手動(dòng)緩存決策?
- var monitor = EqualsMonitorManager
.Get(MonitorConstant.REDIS_KEY, x => x.TableName == tableName); - if (monitor != null)
- {
- //todo something
- }
手動(dòng)決策很簡單,只要嘗試獲取一下即可,獲取到monitor就說明被緩存了,下面就可以直接取緩存了。
如何實(shí)現(xiàn)自動(dòng)緩存決策?
看到第一篇的應(yīng)該對(duì)下面的代碼有印象,我把之前寫的內(nèi)容直接copy過來一份:
SaveChangesAsync是EF的異步保存方法,我們要做的事情其實(shí)很簡單,就是攔截保存方法,代碼中是SaveAsync,這個(gè)是我們自己針對(duì)EF封裝后的方法。
大概思路是這樣的:
想要讓 SaveAsync 聽我們的話, override 就派上了用場(chǎng),重寫 SaveAsync。
調(diào)用基類的 SaveAsync 后,再加上保存到Redis的代碼。
這樣一個(gè)SaveAsync就變成了做2件事,先保存到數(shù)據(jù)庫再保存到Redis,從而杜絕了代碼中到處寫保存到Redis的重復(fù)代碼。
- public class DataWrapper
: EFWrapperBase - where T : class,new()
- {
- public DataWrapper()
- {
- base.Context.EventRegistModel += ModelRegister.Regist;
- }
- public override async Task
SaveAsync() - {
- var result = await base.SaveAsync();
- SaveToRedis();
- return result;
- }
- private void SaveToRedis()
- {
- try
- {
- var type = typeof(T);
- var monitor = EqualsMonitorManager
.Get(MonitorConstant.REDIS_KEY, x => x.TableName == type.Name); - if (monitor != null)
- {
- foreach (var entity in base.DbSet.Local)
- {
- foreach (var field in monitor.Fields)
- {
- var pi = type.GetProperty(field);
- RedisSingleton.GetInstance.Client.HSet(type.Name, string.Format("{0}:{1}", pi.Name, pi.GetValue(entity, null).ToString()), entity);
- }
- }
- }
- }
- catch (Exception ex)
- {
- Logger.Error(ex.ToString());
- }
- }
- }
源碼
源碼地址:http://git.oschina.net/doddgu/PaPaPa
PS:其實(shí)想想真的不難,主要是一種思路,用到的都是基本的C#語法,關(guān)鍵在于你敢不敢想,而我敢想了,你還在猶豫嗎?后面我們會(huì)有更多敢想敢做的事,歡迎你的加入。
原文出自:http://www.cnblogs.com/doddgu/p/papapa_huancunjuece.html
網(wǎng)頁名稱:【PaPaPa】實(shí)現(xiàn)緩存決策:讓你的緩存變的有智慧
URL網(wǎng)址:http://fisionsoft.com.cn/article/coedhph.html


咨詢
建站咨詢
