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除單純緩存作用外,還可以處理一些簡單的邏輯運(yùn)算倔韭,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)閉正在運(yùn)行的memcached服務(wù)
-d install 安裝memcached服務(wù)
-d uninstall 卸載memcached服務(wù)
-u 以身份運(yùn)行 (僅在以root運(yùn)行的時候有效)
-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();
}
}
}