之前在網上看過memcache-mutex的場景分析和實作代碼,這裡将.net方式加以實作,當然這裡主要是依據原文的僞代碼照貓畫虎,以此做為總結及記錄。如果您對相應實作感興趣可以嘗試使用本文提供的代碼進行測試,如果有問題請及時與我聯系。
為了實作原文中的對象到期時間屬性,定義了一個基類,其資訊如下:

[Serializable]
public class CacheObj
{
/// <summary>
/// 資料絕對到期時間,預設為目前時間開始三分鐘後失效
/// </summary>
public DateTime ExpireTime = DateTime.Now.AddMinutes(3);
/// 資料相對有效時間,機關:秒。預設為30秒有效期
public int TimeOut = 30;
}

這樣所有要放到memcached的對象隻要繼承該對象就OK了,比如下面的使用者資訊類:

/// <summary>
/// 使用者資訊
/// </summary>
public class UserInfo : CacheObj
public string UserName;
public int Age;
public string Email;
public override string ToString()
{
return "UserName:" + UserName + " Age:" + Age + " Email:" + Email;
}

下面是原文中方式一的實作代碼:

MemcachedClient mc = MemCachedManager.CacheClient;
//方一
public UserInfo GetCacheData1(string key)
UserInfo value = mc.Get(key) as UserInfo;
if (value == null)
{
// 3 分鐘到期.在delete操作執行之前,目前key_mutex add隻能被添加一次并傳回true
if (mc.Add(key + "_mutex", key + "_mutex", DateTime.Now.AddMinutes(3)) == true)
value = new UserInfo() { UserName = "daizhj", Email = "[email protected]" };// db.get(key);//從加載資料
mc.Set(key, value);
mc.Delete(key + "_mutex");
else
System.Threading.Thread.Sleep(500);//如果設定過短,可能上面set文法還未生效
value = mc.Get(key) as UserInfo;//sleep之後重試讀取cache資料
}
return value;

下面是方式2的代碼:

//方法二
public UserInfo GetCacheData2(string key)
// 3 分鐘到期,在delete之前,目前key_mutex add隻能被添加一次并傳回true
if (mc.Add(key + "_mutex", "add_mutex", DateTime.Now.AddMinutes(3)) == true)
mc.Delete(key + "_mutex");
else
if (value.ExpireTime <= DateTime.Now)
//有值但已過期
if (mc.Add(key + "_mutex", "add_mutex", DateTime.Now.AddMinutes(3)) == true)
{
value.ExpireTime = DateTime.Now.AddSeconds(value.TimeOut);
//這隻是為了讓它先暫時有效(後面即将更新該過期資料),這樣做主要防止避免cache失效時刻大量請求擷取不到mutex并進行sleep,注意這裡設定成有效會導緻其它線程會暫時讀到髒資料
mc.Set(key, value, DateTime.Now.AddSeconds(value.TimeOut * 2));//這裡*2是為了讓memcached緩存資料更長時間,因為真正校驗到期時間用ExpireTime來判斷
//從資料源加載最新資料
value = new UserInfo() { UserName = "daizhenjun", Email = "[email protected]" };// db.get(key);
mc.Set(key, value, DateTime.Now.AddSeconds(value.TimeOut * 2));
mc.Delete(key + "_mutex");
}
else
System.Threading.Thread.Sleep(500);//如果設定過短,可能上面set文法還未生效
value = mc.Get(key) as UserInfo;//sleep之後重試讀取cache資料

無論使用那種方式,都會帶來代碼複雜性增大(尤其第二種),另外還有就是與memcached額外的連接配接及存儲開銷(key_mutex本身存儲也要消耗資源)。因為除非是高并發場景下同時更新memcached,否則這兩種方式需要斟酌使用。