Net 內置記憶體緩存 asp.net 中是有緩存的實現:HttpContext.Cache,緩存的數據是放到 Web 伺服器的進程 記憶體里。 在控制台、WinForm、子線程、SignalR 等不支持 HttpContext 的地方還可以使用 MemoryCache.Default(System.R ...
Net 內置記憶體緩存
asp.net 中是有緩存的實現:HttpContext.Cache
,緩存的數據是放到 Web 伺服器的進程 記憶體里。
在控制台、WinForm、子線程、SignalR 等不支持 HttpContext 的地方還可以使用 MemoryCache.Default(System.Runtime.Caching 這個程式集中) ,HttpContext.Cache 其實就是 對 MemoryCache 的封裝。
//寫入:
MemoryCache.Default.Add("age", 666, DateTimeOffset.Now.AddMinutes(1));
//讀取:
if(MemoryCache.Default.Contains("name"))
{
int age = (int)MemoryCache.Default["age"];
}
進程內緩存最大的優點就是效率高。在可預期數據量不大的情況下推薦使用。 如果數據量比較大或者集群伺服器比較多,就要用單獨的分散式緩存了,也就是搞一臺 或者多台專門伺服器保存緩存數據,所有伺服器都訪問分散式緩存伺服器。
Memcached
簡介
Memcached 是一個專門用來做緩存的伺服器,而且緩存的數據都在記憶體中。Memcached 就相當於一個 Dictionary 鍵值對集合,保存的是鍵值對,然後根據 key 取 value。 當然 web 伺服器和 Memcached 之間還是要網路間通訊,效率還是沒有進程內緩存效率 高。Memcached 程式重啟之後數據就會消失。
安裝
memcached.exe -d install
卸載
memcached.exe -d uninstall
.Net 連接 memcached 安裝
Memcached 的.Net 開發包:Install-Package EnyimMemcached
- Memcache 存入的是鍵值對。Memcache 存入數據的 3 中模式 Set、Replace、Add,根據名 字就能猜出來:
- Set:存在則覆蓋,不存在則新增
- Replace:如果存在則覆蓋,並且返回 true;如果不存在則不處理,並且返回 false;
- Add:如果不存在則新增,並且返回 true;如果存在則不處理,並且返回 false;
沒特殊要求一般用 Set 就可以了。
MemcachedClientConfiguration mcConfig = new MemcachedClientConfiguration(); mcConfig.AddServer("127.0.0.1:11211");
//必須指定埠
using (MemcachedClient client = new MemcachedClient(mcConfig))
{
client.Store(Enyim.Caching.Memcached.StoreMode.Set, "name", "rsfy");
}
如果保存普通類對象,則對象必須可序列化(不同 Memcached 客戶端保存對象的機制 都不盡相同)。
2)存入設置過期時間 設置最後一個 TimeSpan 類型的參數:
client.Store(Enyim.Caching.Memcached.StoreMode.Set, "name", "yzk",TimeSpan.FromSeconds(5));
如果之前對於同一個 Key 設置過一個過期時間,之後又設置過一個,以最後一次的為準。
3)讀取:如果找不到,則返回 null
。
client.Get("name");
當然也可以用
public bool TryGet(string key, out object value)
當然還支持泛型的
public T Get<T>(string key)
- Remove(string key)則是刪除一個 key 對應的內容。 Key 的長度最高是 250 個字元,Value 最長 1M。 與 Store、Get、Remove 配套的還有 ExecuteXXX 方法,唯一區別就是返回值信息更詳細。
5)Key 的選擇: Memcaced就相當於一個大鍵值對,不同系統放到Memcached中的數據都是不隔離的, 因此設定 Key 的時候要選擇好 Key,這樣就不容易衝突。建議規則“系統名字_模塊名字_業務_Key”,比如“Shop_Admin_FilterWords”
6) Increment、Decrement 是用來對計數器進行增減的,不過用得少。用 Redis 更合適。
Cas 操作:
用來解決併發問題:讀出一個值,做一些判斷或者處理,再寫回,有可能有併發的問題。 Cas 是 Memcached 1.2.5 之後引入的特性,類似於資料庫的“樂觀鎖”,查詢的時候查出一個 cas 值,在寫入的時候帶著這個 cas 值,如果發現 cas 值已經變了,則說明已經有別人改過了。 下麵的程式:
var cas = client.GetWithCas("Name");
Console.WriteLine("按任意鍵繼續");
Console.ReadKey();
var res = client.Cas(Enyim.Caching.Memcached.StoreMode.Set, "Name", cas.Result + "1", cas.Cas);
if(res.Result) {
Console.WriteLine("修改成功");
} else {
Console.WriteLine("被別人改了");
}
啟動兩個實例,測試效果。 Memcached 一般就是做緩存用,因此也不要用這個 Cas。
memcached 的集群
memcached 重啟之後短時間內大量的請求會涌入資料庫,給資料庫造成壓力,解決這 個的方法就是使用集群,有多台 Memcached 伺服器提供服務。 當 memcached 伺服器壓力大了之後也有必要搞 memcached 集群來分擔壓力。 Memcached 伺服器的“雪崩”問題:如果所有緩存設置過期時間一樣,那麼每隔一段 時間就會造成一次資料庫訪問的高峰:
**解決的方法就是緩存時間設置不一樣,比如加上一個隨機數。 **
Memcached 的集群實現很簡單,集群節點直接不進行通訊、同步,只要在多個伺服器 上啟動多個 Memcached 伺服器即可,客戶端決定把數據寫入不同的實例,不搞主從複製, 每個資料庫實例保存一部分內容。 然後 mcConfig.AddServer("127.0.0.1:11211");添加多個伺服器 ip 地址,然後客戶端根據 自己的演算法決定把數據寫入哪個 Memcached 實例,取資料庫的時候再根據同樣的定位演算法 去哪台伺服器上去取。 節點定位演算法有很多種,最常用的有兩種 Ketama、VBucket。Ketama 是根據 Key 算出一 個 hash 值,根據 hash 值再算到伺服器;而 VBucket 也是根據 key 算出 hash 值,但是不是直 接根據 hash 值算出服務地址,而是維護一個 VBucket 表,在表中指定不同的 hash 值由不同 的伺服器處理,還可以臨時改變指向。建議用 Ketama 就可以了。節點定位演算法會自動處理 故障伺服器。
mcConfig.NodeLocatorFactory = new KetamaNodeLocatorFactory()。
緩存要求都 不高。
Memcached 封裝示例
public static class MemcachedHelper
{
public static readonly MemcachedHelperClientConfiguration mcConfig = new MemcachedClientConfiguration();
public static readonly MemcachedClient client = null;
static MemcachedHelper()
{
mcConfig.AddServer("127.0.0.1:11211");
client = new MemcachedClient(mcConfig);
}
public static T GetOrSet<T>(string key, object value) where T : class
{
var getdata = Get<T>(key);
if (getdata != null)
{
return getdata;
}
else
{
Set(key, value);
return Get<T>(key);
}
}
public static void Set(string key, object value)
{
client.Store(Enyim.Caching.Memcached.StoreMode.Set, key, JsonConvert.SerializeObject(value));
}
public static T Get<T>(string key) where T : class
{
try
{
return JsonConvert.DeserializeObject<T>(client.Get(key).ToString());
}
catch (Exception e)
{
return null;
}
}