Memcached簡介
Memcached 是一個高性能的分布式內(nèi)存對象緩存系統(tǒng),用于動態(tài)Web應(yīng)用以減輕數(shù)據(jù)庫負(fù)載。它通過在內(nèi)存中緩存數(shù)據(jù)和對象來減少讀取數(shù)據(jù)庫的次數(shù),從而提高動態(tài)、數(shù)據(jù)庫驅(qū)動網(wǎng)站的速度。Memcached基于一個存儲鍵/值對的hashmap。其守護(hù)進(jìn)程(daemon )是用C寫的,但是客戶端可以用任何語言來編寫,并通過memcached協(xié)議與守護(hù)進(jìn)程通信。天生支持集群。
目前有多種平臺的Memcached版本,比如Linux、FreeBSD、Solaris 、Mac OS X及Windows平臺。
官方網(wǎng)站:http://memcached.org/
我們安裝Windows版本來演示。
32bit:下載 memcached-win32-1.4.4-14.zip
64bit:如果需要win64版,下載 memcached-win64-1.4.4-14.zip
Memcached 內(nèi)存管理機(jī)制:
Menceched 通過預(yù)分配指定的內(nèi)存空間來存取數(shù)據(jù),所有的數(shù)據(jù)都保存在 memcached 內(nèi)置的內(nèi)存中。利用 Slab Allocation 機(jī)制來分配和管理內(nèi)存。按照預(yù)先規(guī)定的大小,將分配的內(nèi)存分割成特定長度的內(nèi)存塊,再把尺寸相同的內(nèi)存塊分成組,這些內(nèi)存塊不會釋放,可以重復(fù)利用。當(dāng)存入的數(shù)據(jù)占滿內(nèi)存空間時,Memcached 使用 LRU 算法自動刪除不是用的緩存數(shù)據(jù),即重用過期數(shù)據(jù)的內(nèi)存空間。Memcached 是為緩存系統(tǒng)設(shè)計的,因此沒有考慮數(shù)據(jù)的容災(zāi)問題,和機(jī)器的內(nèi)存一樣,重啟機(jī)器將會丟失,如果希望服務(wù)重啟數(shù)據(jù)依然能保留,那么就需要 sina 網(wǎng)開發(fā)的 Memcachedb 持久性內(nèi)存緩沖系統(tǒng),當(dāng)然還有常見的 NOSQL 服務(wù)如 redis。默認(rèn)監(jiān)聽端口:11211
Memcached數(shù)據(jù)訪問模型
-
添加新的鍵值對數(shù)據(jù)
從圖中可以看出,Memcached雖然稱為“分布式”緩存服務(wù)器,但服務(wù)器端并沒有“分布式”功能,而是完全由客戶端程序庫實現(xiàn)的。服務(wù)端之間沒有任何聯(lián)系,數(shù)據(jù)存取都是通過客戶端的算法實現(xiàn)的。當(dāng)客戶端要存取數(shù)據(jù)時,首先會通過算法查找自己維護(hù)的服務(wù)器哈希列表,找到對應(yīng)的服務(wù)器后,再將數(shù)據(jù)存往指定服務(wù)器。例如:上圖中應(yīng)用程序要新增一個<'tokyo',data>的鍵值對,它同過set操作提交給Memcached客戶端,客戶端通過一定的哈希算法(比如:一般的求余函數(shù)或者強(qiáng)大的一致性Hash算法)從服務(wù)器列表中計算出一個要存儲的服務(wù)器地址,最后將該鍵值對存儲到計算出來的服務(wù)器里邊。 -
獲取已存在的鍵值對數(shù)據(jù)
上圖中應(yīng)用程序想要獲取Key為‘tokyo’的Value,于是它向Memcached客戶端提交了一個Get請求,Memcached客戶端還是通過算法從服務(wù)器列表查詢哪臺服務(wù)器存有Key為‘tokyo’的Value(即選擇剛剛Set到了哪臺服務(wù)器),如果查到,則向查到的服務(wù)器請求返回Key為‘tokyo’的數(shù)據(jù)。
Memcached分布式的核心—一致性Hash算法
一致性Hash算法是分布式緩存的核心理論,我也學(xué)習(xí)得不深入,也只是剛剛了解了一下,后面我有空深入學(xué)習(xí)一下。
首先,簡單的路由算法(通過使用余數(shù)Hash)無法滿足業(yè)務(wù)發(fā)展時服務(wù)器擴(kuò)容的需要:緩存命中率下降。例如:當(dāng)3臺服務(wù)器擴(kuò)容至4臺時,采用普通的余數(shù)Hash算法會導(dǎo)致大約75%(3/4)被緩存了的數(shù)據(jù)無法正確命中,隨著服務(wù)器集群規(guī)模的增大,這個比例會線性地上升。那么,可以想象,當(dāng)100臺服務(wù)器的集群中加入一臺服務(wù)器,不能命中的概率大概是99%(N/N+1),這個結(jié)果顯然是無法接受的。那么,能否通過改進(jìn)路由算法,使得新加入的服務(wù)器不影響大部分緩存數(shù)據(jù)的正確性呢?請看下面的一致性Hash算法。
一致性Hash算法通過一個叫做一致性Hash環(huán)的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)KEY到緩存服務(wù)器的Hash映射,如下圖所示:

具體算法過程是:
- 先構(gòu)造一個長度為0~2^32(2的32次冪)個的整數(shù)環(huán)(又稱:一致性Hash環(huán)),根據(jù)節(jié)點名稱的Hash值將緩存服務(wù)器節(jié)點放置在這個Hash環(huán)中,如上圖中的node1,node2等;
- 根據(jù)需要緩存的數(shù)據(jù)的KEY值計算得到其Hash值,如上圖中右半部分的“鍵”,計算其Hash值后離node2很近;
- 在Hash環(huán)上順時針查找距離這個KEY的Hash值最近的緩存服務(wù)器節(jié)點,完成KEY到服務(wù)器的Hash映射查找,如上圖中離右邊這個鍵的Hash值最近的順時針方向的服務(wù)器節(jié)點是node2,因此這個KEY會到node2中讀取數(shù)據(jù);
當(dāng)緩存服務(wù)器集群需要擴(kuò)容的時候,只需要將新加入的節(jié)點名稱(如node5)的Hash值放入一致性Hash環(huán)中,由于KEY總是順時針查找距離其最近的節(jié)點,因此新加入的節(jié)點只影響整個環(huán)中的一部分。如下圖中所示,添加node5后,只影響右邊逆時針方向的三個Key/Value對數(shù)據(jù),只占整個Hash環(huán)中的一小部分。

因此,我們可以與之前的普通余數(shù)Hash作對比:采用一致性Hash算法時,當(dāng)3臺服務(wù)器擴(kuò)容到4臺時,可以繼續(xù)命中原有緩存數(shù)據(jù)的概率為75%,遠(yuǎn)高于普通余數(shù)Hash的25%,而且隨著集群規(guī)模越大,繼續(xù)命中原有緩存數(shù)據(jù)的概率也會隨之增大。當(dāng)100臺服務(wù)器增加1臺時,繼續(xù)命中的概率是99%。雖然,仍有小部分?jǐn)?shù)據(jù)緩存在服務(wù)器中無法被讀取到,但是這個比例足夠小,通過訪問數(shù)據(jù)庫也不會對數(shù)據(jù)庫造成致命的負(fù)載壓力。
Memcached與Redis的對比
- 沒有必要過多的關(guān)心性能,因為二者的性能都已經(jīng)足夠高了。由于Redis只使用單核,而Memcached可以使用多核,所以在比較上,平均每一個核上Redis在存儲小數(shù)據(jù)時比Memcached性能更高。而在100k以上的數(shù)據(jù)中,Memcached性能要高于Redis,雖然Redis最近也在存儲大數(shù)據(jù)的性能上進(jìn)行優(yōu)化,但是比起Memcached,還是稍有遜色。說了這么多,結(jié)論是,無論你使用哪一個,每秒處理請求的次數(shù)都不會成為瓶頸。(比如瓶頸可能會在網(wǎng)卡)
- 如果要說內(nèi)存使用效率,使用簡單的key-value存儲的話,Memcached的內(nèi)存利用率更高,而如果Redis采用hash結(jié)構(gòu)來做key-value存儲,由于其組合式的壓縮,其內(nèi)存利用率會高于Memcached。當(dāng)然,這和你的應(yīng)用場景和數(shù)據(jù)特性有關(guān)。
- 如果你對數(shù)據(jù)持久化和數(shù)據(jù)同步有所要求,那么推薦你選擇Redis,因為這兩個特性Memcached都不具備。即使你只是希望在升級或者重啟系統(tǒng)后緩存數(shù)據(jù)不會丟失,選擇Redis也是明智的。
因此,我們可以得出一個結(jié)論:在簡單的Key/Value應(yīng)用場景(例如緩存),Memcached擁有更高的讀寫性能;而在數(shù)據(jù)持久化和數(shù)據(jù)同步場景,Redis擁有更加強(qiáng)大的功能和更為豐富的數(shù)據(jù)類型;
需要慎重考慮的部分
- Memcached單個key-value大小有限,一個value最大只支持1MB,而Redis最大支持512MB
- Memcached只是個內(nèi)存緩存,對可靠性無要求;而Redis更傾向于內(nèi)存數(shù)據(jù)庫,因此對對可靠性方面要求比較高
- 從本質(zhì)上講,Memcached只是一個單一key-value內(nèi)存Cache;而Redis則是一個數(shù)據(jù)結(jié)構(gòu)內(nèi)存數(shù)據(jù)庫,支持五種數(shù)據(jù)類型,因此Redis除單純緩存作用外,還可以處理一些簡單的邏輯運算,Redis不僅可以緩存,而且還可以作為數(shù)據(jù)庫用
- 新版本(3.0)的Redis是指集群分布式,也就是說集群本身均衡客戶端請求,各個節(jié)點可以交流,可拓展行、可維護(hù)性更強(qiáng)大。
- MongoDB不支持事務(wù)。
安裝和啟動
- install
memcached.exe -d install
- start
memcached.exe -d start
- stop
memcached.exe -d stop
- unInstall
memcached.exe -d uninstall
以上的安裝和啟動都是在默認(rèn)環(huán)境下進(jìn)行的,在安裝時可設(shè)置如下參數(shù):
Memcached默認(rèn)使用端口是11211
默認(rèn)最大連接數(shù)是1024個
默認(rèn)最大使用內(nèi)存是64M
默認(rèn)每個鍵值對,值存儲空間為1M
-p 監(jiān)聽的端口
-l 連接的IP地址, 默認(rèn)是本機(jī)
-d start 啟動memcached服務(wù)
-d restart 重起memcached服務(wù)
-d stop|shutdown 關(guān)閉正在運行的memcached服務(wù)
-d install 安裝memcached服務(wù)
-d uninstall 卸載memcached服務(wù)
-u 以身份運行 (僅在以root運行的時候有效)
-m 最大內(nèi)存使用,單位MB。默認(rèn)64MB **
-M 內(nèi)存耗盡時返回錯誤,而不是刪除項
-c 最大同時連接數(shù),默認(rèn)是1024 **
-f 塊大小增長因子,默認(rèn)是1.25
-n 最小分配空間,key+value+flags默認(rèn)是48
-h 顯示幫助
OK,命令咱們已經(jīng)執(zhí)行完了.怎么才知道Memcached服務(wù)已經(jīng)安裝成功并啟動了呢?
cmd 命令 services.msc

檢測Memcached服務(wù)是否成功啟動:
使用telnet命令連接到登錄臺:telnet 服務(wù)器IP地址 11211(11211是默認(rèn)的Memcached服務(wù)端口號)
打印當(dāng)前Memcache服務(wù)器狀態(tài):stats
stats.png
可以看到,通過stats命令列出了一系列的Memcached服務(wù)狀態(tài)信息,那么這些信息又代表什么意思呢?我們可以通過下圖來知道:
先把memcached用起來
下載客戶端的4個dll,
ICSharpCode.SharpZipLib.dll,log4net.dll,Memcached.ClientLibrary.dll,Commons.dll
跟著我新建一個簡單控制臺應(yīng)用程序
using Memcached.ClientLibrary;
using System;
namespace Tdf.RedisCacheTest
{
class AMemcached
{
public static MemcachedClient cache;
static AMemcached()
{
string[] servers = { "127.0.0.1:11211" };
// 初始化池
SockIOPool pool = SockIOPool.GetInstance();
// 設(shè)置服務(wù)器列表
pool.SetServers(servers);
// 各服務(wù)器之間負(fù)載均衡的設(shè)置比例
pool.SetWeights(new int[] { 1 });
// 初始化時創(chuàng)建連接數(shù)
pool.InitConnections = 3;
// 最小連接數(shù)
pool.MinConnections = 3;
// 最大連接數(shù)
pool.MaxConnections = 5;
// 連接的最大空閑時間,下面設(shè)置為6個小時(單位ms),超過這個設(shè)置時間,連接會被釋放掉
pool.MaxIdle = 1000 * 60 * 60 * 6;
// socket連接的超時時間,下面設(shè)置表示不超時(單位ms),即一直保持鏈接狀態(tài)
pool.SocketConnectTimeout = 0;
// 通訊的超時時間,下面設(shè)置為3秒(單位ms),.Net版本沒有實現(xiàn)
pool.SocketTimeout = 1000 * 3;
// 維護(hù)線程的間隔激活時間,下面設(shè)置為30秒(單位s),設(shè)置為0時表示不啟用維護(hù)線程
pool.MaintenanceSleep = 30;
// 設(shè)置SocktIO池的故障標(biāo)志
pool.Failover = true;
// 是否對TCP/IP通訊使用nalgle算法,.net版本沒有實現(xiàn)
pool.Nagle = false;
// socket單次任務(wù)的最大時間(單位ms),超過這個時間socket會被強(qiáng)行中端掉,當(dāng)前任務(wù)失敗。
pool.MaxBusy = 1000 * 10;
pool.Initialize();
cache = new MemcachedClient();
// 是否啟用壓縮數(shù)據(jù):如果啟用了壓縮,數(shù)據(jù)壓縮長于門檻的數(shù)據(jù)將被儲存在壓縮的形式
cache.EnableCompression = false;
// 壓縮設(shè)置,超過指定大小的都壓縮
cache.CompressionThreshold = 1024 * 1024;
}
}
class Program
{
static void Main(string[] args)
{
// 存入key為userName,value為Bobby的一個緩存
AMemcached.cache.Add("userName", "Bobby");
// 讀出key為a的緩存值
var s = AMemcached.cache.Get("userName");
// 輸出
Console.WriteLine(s);
Console.Read();
}
}
}
細(xì)品 .NET Memcached.ClientLibrary
說說memcached分布式緩存的設(shè)置與應(yīng)用
string[] servers = { "127.0.0.1:11211", "192.168.2.100:11211" };
// 初始化池
SockIOPool pool = SockIOPool.GetInstance();
// 設(shè)置服務(wù)器列表
pool.SetServers(servers);
// 各服務(wù)器之間負(fù)載均衡的設(shè)置比例
pool.SetWeights(new int[] { 1, 10 });
Note:
- 在172.18.5.66,與192.168.10.121兩臺機(jī)器上裝memcached服務(wù)端。
- pool.SetWeights這里的1跟10意思是,負(fù)載均衡比例,假如11000條數(shù)據(jù),大致數(shù)據(jù)分布為:172.18.5.66分布1000條數(shù)據(jù)左右。另外一臺為10000條左右。
- memcached服務(wù)端并不具備負(fù)載均衡的能力,而是memcachedClient實現(xiàn)的,具體存取數(shù)據(jù)實現(xiàn)的核心是采用一致性Hash算法,把key-value分布到某一臺服務(wù)器中里邊。
說說memcached的數(shù)據(jù)壓縮機(jī)制
// 是否啟用壓縮數(shù)據(jù):如果啟用了壓縮,數(shù)據(jù)壓縮長于門檻的數(shù)據(jù)將被儲存在壓縮的形式
cache.EnableCompression = false;
// 壓縮設(shè)置,超過指定大小的都壓縮
cache.CompressionThreshold = 1024 * 1024;
Note:
- 這個處理是在MemcachedClient對象中,設(shè)置這個EnableCompression屬性,是否使用壓縮的意思,如果啟用啦壓縮功能 ,則ICSharpCode.SharpZipLib類庫會在數(shù)據(jù)超過預(yù)設(shè)大小時,進(jìn)行數(shù)據(jù)壓縮處理。
- CompressionThreshold這個屬性是壓縮的閥值,默認(rèn)是15K,如果超過設(shè)定的閥值則使用memcached的通訊協(xié)議,存數(shù)據(jù)時給每個數(shù)據(jù)項分配一個16為的flag表示,用作記錄是否有壓縮,如果有壓縮則提取數(shù)據(jù)是進(jìn)行解壓。如果沒有超過閥值則不壓縮,直接存儲。
說說怎么使用客戶端多個SocketIO池
using Memcached.ClientLibrary;
using System;
namespace Tdf.RedisCacheTest
{
class AMemcached
{
public MemcachedClient cache;
public AMemcached(string poolName)
{
string[] servers = { "127.0.0.1:11211", "192.168.2.100:11211" };
// 初始化池
SockIOPool pool = SockIOPool.GetInstance();
// 設(shè)置服務(wù)器列表
pool.SetServers(servers);
// 各服務(wù)器之間負(fù)載均衡的設(shè)置比例
pool.SetWeights(new int[] { 1, 10 });
// 初始化時創(chuàng)建連接數(shù)
pool.InitConnections = 3;
// 最小連接數(shù)
pool.MinConnections = 3;
// 最大連接數(shù)
pool.MaxConnections = 5;
// 連接的最大空閑時間,下面設(shè)置為6個小時(單位ms),超過這個設(shè)置時間,連接會被釋放掉
pool.MaxIdle = 1000 * 60 * 60 * 6;
// socket連接的超時時間,下面設(shè)置表示不超時(單位ms),即一直保持鏈接狀態(tài)
pool.SocketConnectTimeout = 0;
// 通訊的超時時間,下面設(shè)置為3秒(單位ms),.Net版本沒有實現(xiàn)
pool.SocketTimeout = 1000 * 3;
// 維護(hù)線程的間隔激活時間,下面設(shè)置為30秒(單位s),設(shè)置為0時表示不啟用維護(hù)線程
pool.MaintenanceSleep = 30;
// 設(shè)置SocktIO池的故障標(biāo)志
pool.Failover = true;
// 是否對TCP/IP通訊使用nalgle算法,.net版本沒有實現(xiàn)
pool.Nagle = false;
// socket單次任務(wù)的最大時間(單位ms),超過這個時間socket會被強(qiáng)行中端掉,當(dāng)前任務(wù)失敗。
pool.MaxBusy = 1000 * 10;
pool.Initialize();
cache = new MemcachedClient();
// 是否啟用壓縮數(shù)據(jù):如果啟用了壓縮,數(shù)據(jù)壓縮長于門檻的數(shù)據(jù)將被儲存在壓縮的形式
cache.EnableCompression = false;
// 壓縮設(shè)置,超過指定大小的都壓縮
cache.CompressionThreshold = 1024 * 1024;
}
}
class Program
{
static void Main(string[] args)
{
// 存入key為userName,value為Bobby的一個緩存
new AMemcached("poolName").cache.Add("b", 123);
// AMemcached.cache.Add("userName", "Bobby");
// 讀出key為a的緩存值
var s = new AMemcached("poolName").cache.Get("userName");
// 輸出
Console.WriteLine(s);
Console.Read();
}
}
}
說說memcached的故障轉(zhuǎn)移處理
// 設(shè)置SocktIO池的故障標(biāo)志
pool.Failover = true;
Note:memcached的鼓掌轉(zhuǎn)移是一套正常節(jié)點發(fā)生故障變?yōu)樗拦?jié)點時的處理機(jī)制。
- 開啟故障轉(zhuǎn)移:如果發(fā)生socket異常,則該節(jié)點被添加到存放死節(jié)點屬性的_hostDead中,新請求被映射到dead server,檢測嘗試連接死節(jié)點的時間間隔屬性_hostDeadDuration(默認(rèn)設(shè)置為100ms),如果沒有達(dá)到設(shè)定的間隔時間則key會被映射到可用的server處理,如果達(dá)到了時間間隔,則嘗試重新鏈接,連接成功將此節(jié)點從_hostDead中去除,連接失敗則間隔時間翻倍存放,下次重新連接時間會被拉長。
- 不開啟故障轉(zhuǎn)移:新的請求都會被映射到dead server上,嘗試重新建立socket鏈接,如果連接失敗,返回null或者操作失敗。
說說key-value中的key與value
- key在服務(wù)端的長度限制為250個字符,建議使用較短的key但不要重復(fù)。
- value的大小限制為1mb,如果大拉,可以使用壓縮,如果還大,那可能拆分到多個key中。
Memcached 客戶端使用封裝
抽象出來了一個接口:
using System;
namespace Tdf.Memcached
{
public interface IMemcached
{
void Add(string key, object value);
void Add(string key, object value, DateTime expiredDateTime);
void Update(string key, object value);
void Update(string key, object value, DateTime expiredDateTime);
void Set(string key, object value);
void Set(string key, object value, DateTime expiredTime);
void Delete(string key);
object Get(string key);
bool KeyExists(string key);
}
}
MemcacheHelper.cs
using Memcached.ClientLibrary;
using System;
using System.Configuration;
namespace Tdf.Memcached
{
/// <summary>
/// 基于Memcached.ClientLibrary封裝使用Memchached信息
/// 讀取緩存存放在服務(wù)器
/// </summary>
public class MemcacheHelper
{
/// <summary>
/// 字段_instance,存放注冊的緩存信息
/// </summary>
private static MemcacheHelper _instance;
/// <summary>
/// 緩存客戶端
/// </summary>
private readonly MemcachedClient _client;
/// <summary>
/// 受保護(hù)類型的緩存對象,初始化一個新的緩存對象
/// </summary>
protected MemcacheHelper()
{
// 讀取app.Config中需要緩存的服務(wù)器地址信息,可以傳遞多個地址,使用";"分隔
string[] serverList = ConfigurationManager.AppSettings["readWriteHosts"].Split(new char[] { ';' });
try
{
// 初始化池
var sockIoPool = SockIOPool.GetInstance();
// 設(shè)置服務(wù)器列表
sockIoPool.SetServers(serverList);
// 各服務(wù)器之間負(fù)載均衡的設(shè)置比例
sockIoPool.SetWeights(new int[] { 1 });
// 初始化時創(chuàng)建連接數(shù)
sockIoPool.InitConnections = 3;
// 最小連接數(shù)
sockIoPool.MinConnections = 3;
// 最大連接數(shù)
sockIoPool.MaxConnections = 5;
// 連接的最大空閑時間,下面設(shè)置為6個小時(單位ms),超過這個設(shè)置時間,連接會被釋放掉
sockIoPool.MaxIdle = 1000 * 60 * 60 * 6;
// socket連接的超時時間,下面設(shè)置表示不超時(單位ms),即一直保持鏈接狀態(tài)
sockIoPool.SocketConnectTimeout = 0;
// 通訊的超時時間,下面設(shè)置為3秒(單位ms),.Net版本沒有實現(xiàn)
sockIoPool.SocketTimeout = 1000 * 3;
// 維護(hù)線程的間隔激活時間,下面設(shè)置為30秒(單位s),設(shè)置為0時表示不啟用維護(hù)線程
sockIoPool.MaintenanceSleep = 30;
// 設(shè)置SocktIO池的故障標(biāo)志
sockIoPool.Failover = true;
// 是否對TCP/IP通訊使用nalgle算法,.net版本沒有實現(xiàn)
sockIoPool.Nagle = false;
// socket單次任務(wù)的最大時間(單位ms),超過這個時間socket會被強(qiáng)行中端掉,當(dāng)前任務(wù)失敗。
sockIoPool.MaxBusy = 1000 * 10;
sockIoPool.Initialize();
// 實例化緩存對象
_client = new MemcachedClient();
// 是否啟用壓縮數(shù)據(jù):如果啟用了壓縮,數(shù)據(jù)壓縮長于門檻的數(shù)據(jù)將被儲存在壓縮的形式
_client.EnableCompression = false;
// 壓縮設(shè)置,超過指定大小的都壓縮
_client.CompressionThreshold = 1024 * 1024;
}
catch (Exception ex)
{
// 錯誤信息寫入事務(wù)日志
throw new Exception(ex.Message);
}
}
/// <summary>
/// 獲取緩存的實例對象,方法調(diào)用的時候使用
/// </summary>
/// <returns></returns>
public static MemcacheHelper GetInstance()
{
return _instance;
}
/// <summary>
/// 添加緩存信息(如果存在緩存信息則直接重寫設(shè)置,否則添加)
/// 使用:MemcacheHelper.GetInstance().Add(key,value)
/// </summary>
/// <param name="key">需要緩存的鍵</param>
/// <param name="value">需要緩存的值</param>
public void Add(string key, object value)
{
if (_client.KeyExists(key))
{
_client.Set(key, value);
}
_client.Add(key, value);
}
/// <summary>
/// 添加緩存信息
/// 使用:MemcacheHelper.GetInstance().Add(key,value,Datetime.Now())
/// </summary>
/// <param name="key">需要緩存的鍵</param>
/// <param name="value">需要緩存的值</param>
/// <param name="expiredDateTime">設(shè)置的緩存的過時時間</param>
public void Add(string key, object value, DateTime expiredDateTime)
{
_client.Add(key, value, expiredDateTime);
}
/// <summary>
/// 修改緩存的值
/// 使用:MemcacheHelper.GetInstance().Update(key,value)
/// </summary>
/// <param name="key">需要修改的鍵</param>
/// <param name="value">需要修改的值</param>
public void Update(string key, object value)
{
_client.Replace(key, value);
}
/// <summary>
/// 修改緩存的值
/// 使用:MemcacheHelper.GetInstance().Update(key,value,Datetime.Now())
/// </summary>
/// <param name="key">需要修改的鍵</param>
/// <param name="value">需要修改的值</param>
/// <param name="expiredDateTime">設(shè)置的緩存的過時時間</param>
public void Update(string key, object value, DateTime expiredDateTime)
{
_client.Replace(key, value, expiredDateTime);
}
/// <summary>
/// 設(shè)置緩存
/// 使用:MemcacheHelper.GetInstance().Set(key,value)
/// </summary>
/// <param name="key">設(shè)置緩存的鍵</param>
/// <param name="value">設(shè)置緩存的值</param>
public void Set(string key, object value)
{
_client.Set(key, value);
}
/// <summary>
/// 設(shè)置緩存,并修改過期時間
/// 使用:MemcacheHelper.GetInstance().Set(key,value,Datetime.Now())
/// </summary>
/// <param name="key">設(shè)置緩存的鍵</param>
/// <param name="value">設(shè)置緩存的值</param>
/// <param name="expiredTime">設(shè)置緩存過期的時間</param>
public void Set(string key, object value, DateTime expiredTime)
{
_client.Set(key, value, expiredTime);
}
/// <summary>
/// 刪除緩存
/// 使用:MemcacheHelper.GetInstance().Delete(key)
/// </summary>
/// <param name="key">需要刪除的緩存的鍵</param>
public void Delete(string key)
{
_client.Delete(key);
}
/// <summary>
/// 獲取緩存的值
/// 使用:MemcacheHelper.GetInstance().Get(key)
/// </summary>
/// <param name="key">傳遞緩存中的鍵</param>
/// <returns>返回緩存在緩存中的信息</returns>
public object Get(string key)
{
return _client.Get(key);
}
/// <summary>
/// 緩存是否存在
/// 使用:MemcacheHelper.GetInstance().KeyExists(key)
/// </summary>
/// <param name="key">傳遞緩存中的鍵</param>
/// <returns>如果為true,則表示存在此緩存,否則比表示不存在</returns>
public bool KeyExists(string key)
{
return _client.KeyExists(key);
}
/// <summary>
/// 注冊Memcache緩存(在Global.asax的Application_Start方法中注冊)
/// 使用:MemcacheHelper.RegisterMemcache();
/// </summary>
public static void RegisterMemcache()
{
if (_instance == null)
{
_instance = new MemcacheHelper();
}
}
}
}
App.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<appSettings>
<add key="readWriteHosts" value="127.0.0.1:11211" />
</appSettings>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="ICSharpCode.SharpZipLib" publicKeyToken="1b03e6acf1164f73" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.84.0.0" newVersion="0.84.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Program.cs
using System;
using Tdf.Memcached;
namespace Tdf.MemcachedTest
{
class Program
{
static void Main(string[] args)
{
// 注冊Memcache緩存
MemcacheHelper.RegisterMemcache();
// 添加緩存信息(如果存在緩存信息則直接重寫設(shè)置,否則添加)
MemcacheHelper.GetInstance().Add("userName", "Bobby");
// 緩存是否存在
var tf = MemcacheHelper.GetInstance().KeyExists("userName");
Console.WriteLine(tf);
// 獲取緩存的值
var s = MemcacheHelper.GetInstance().Get("userName");
Console.WriteLine(s);
Console.Read();
}
}
}
到此,我們已經(jīng)完成了一個最小化的memcached集群讀寫測試Demo。但是,在實際的開發(fā)場景中,遠(yuǎn)不僅僅是存儲一個字符串,更多的是存儲一個自定義的類的實例對象。這就需要使用到序列化,下面我們來新加一個類Claim,讓其作為可序列化的對象來存儲進(jìn)Memcached中。注意:需要為該類加上[Serializable]的特性!
using System;
using Tdf.Memcached;
namespace Tdf.MemcachedTest
{
[Serializable]
public class Claim
{
public int UserId { get; set; }
public string UserName { get; set; }
}
class Program
{
static void Main(string[] args)
{
// 注冊Memcache緩存
MemcacheHelper.RegisterMemcache();
// 自定義對象存儲
Claim claim = new Claim();
claim.UserId = 694802856;
claim.UserName = "難念的經(jīng)";
MemcacheHelper.GetInstance().Add("Claim", claim);
Claim newMyObj = MemcacheHelper.GetInstance().Get("Claim") as Claim;
Console.WriteLine("Hello,My UserId is {0} and UserName is {1}", newMyObj.UserId, newMyObj.UserName);
Console.Read();
}
}
}




