一致性Hash算法
從上面的圖中,可以看出一個(gè)很重要的問(wèn)題魏身,就是對(duì)服務(wù)器集群的管理惊橱,路由算法至關(guān)重要,就和負(fù)載均衡算法一樣箭昵,路由算法決定著究竟該訪問(wèn)集群中的哪臺(tái)服務(wù)器税朴,先看一個(gè)簡(jiǎn)單的路由算法。
1家制、余數(shù)Hash
比方說(shuō)正林,字符串str對(duì)應(yīng)的HashCode是50、服務(wù)器的數(shù)目是3颤殴,取余數(shù)得到2觅廓,str對(duì)應(yīng)節(jié)點(diǎn)Node2,所以路由算法把str路由到Node2服務(wù)器上涵但。由于HashCode隨機(jī)性比較強(qiáng)杈绸,所以使用余數(shù)Hash路由算法就可以保證緩存數(shù)據(jù)在整個(gè)MemCache服務(wù)器集群中有比較均衡的分布。
如果不考慮服務(wù)器集群的伸縮性(什么是伸縮性矮瘟,請(qǐng)參見(jiàn)大型網(wǎng)站架構(gòu)學(xué)習(xí)筆記)瞳脓,那么余數(shù)Hash算法幾乎可以滿足絕大多數(shù)的緩存路由需求,但是當(dāng)分布式緩存集群需要擴(kuò)容的時(shí)候澈侠,就難辦了劫侧。
就假設(shè)MemCache服務(wù)器集群由3臺(tái)變?yōu)?臺(tái)吧,更改服務(wù)器列表哨啃,仍然使用余數(shù)Hash烧栋,50對(duì)4的余數(shù)是2,對(duì)應(yīng)Node2棘催,但是str原來(lái)是存在Node1上的劲弦,這就導(dǎo)致了緩存沒(méi)有命中耳标。如果這么說(shuō)不夠明白醇坝,那么不妨舉個(gè)例子,原來(lái)有HashCode為0~19的20個(gè)數(shù)據(jù),那么:
HashCode012345678910111213141516171819
路由到的服務(wù)器01201201201201201201
現(xiàn)在我擴(kuò)容到4臺(tái)呼猪,加粗標(biāo)紅的表示命中:
HashCode012345678910111213141516171819
路由到的服務(wù)器01230123012301230123
如果我擴(kuò)容到20+的臺(tái)數(shù)画畅,只有前三個(gè)HashCode對(duì)應(yīng)的Key是命中的,也就是15%宋距。當(dāng)然這只是個(gè)簡(jiǎn)單例子轴踱,現(xiàn)實(shí)情況肯定比這個(gè)復(fù)雜得多,不過(guò)足以說(shuō)明谚赎,使用余數(shù)Hash的路由算法淫僻,在擴(kuò)容的時(shí)候會(huì)造成大量的數(shù)據(jù)無(wú)法正確命中(其實(shí)不僅僅是無(wú)法命中,那些大量的無(wú)法命中的數(shù)據(jù)還在原緩存中在被移除前占據(jù)著內(nèi)存)壶唤。這個(gè)結(jié)果顯然是無(wú)法接受的雳灵,在網(wǎng)站業(yè)務(wù)中,大部分的業(yè)務(wù)數(shù)據(jù)度操作請(qǐng)求上事實(shí)上是通過(guò)緩存獲取的闸盔,只有少量讀操作會(huì)訪問(wèn)數(shù)據(jù)庫(kù)悯辙,因此數(shù)據(jù)庫(kù)的負(fù)載能力是以有緩存為前提而設(shè)計(jì)的。當(dāng)大部分被緩存了的數(shù)據(jù)因?yàn)榉?wù)器擴(kuò)容而不能正確讀取時(shí)迎吵,這些數(shù)據(jù)訪問(wèn)的壓力就落在了數(shù)據(jù)庫(kù)的身上躲撰,這將大大超過(guò)數(shù)據(jù)庫(kù)的負(fù)載能力,嚴(yán)重的可能會(huì)導(dǎo)致數(shù)據(jù)庫(kù)宕機(jī)击费。
這個(gè)問(wèn)題有解決方案拢蛋,解決步驟為:
(1)在網(wǎng)站訪問(wèn)量低谷,通常是深夜蔫巩,技術(shù)團(tuán)隊(duì)加班瓤狐,擴(kuò)容、重啟服務(wù)器
(2)通過(guò)模擬請(qǐng)求的方式逐漸預(yù)熱緩存批幌,使緩存服務(wù)器中的數(shù)據(jù)重新分布
2础锐、一致性Hash算法
一致性Hash算法通過(guò)一個(gè)叫做一致性Hash環(huán)的數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)Key到緩存服務(wù)器的Hash映射,看一下我自己畫(huà)的一張圖:
具體算法過(guò)程為:先構(gòu)造一個(gè)長(zhǎng)度為232的整數(shù)環(huán)(這個(gè)環(huán)被稱(chēng)為一致性Hash環(huán))荧缘,根據(jù)節(jié)點(diǎn)名稱(chēng)的Hash值(其分布為[0, 232-1])將緩存服務(wù)器節(jié)點(diǎn)放置在這個(gè)Hash環(huán)上皆警,然后根據(jù)需要緩存的數(shù)據(jù)的Key值計(jì)算得到其Hash值(其分布也為[0, 232-1]),然后在Hash環(huán)上順時(shí)針查找距離這個(gè)Key值的Hash值最近的服務(wù)器節(jié)點(diǎn)截粗,完成Key到服務(wù)器的映射查找信姓。
就如同圖上所示,三個(gè)Node點(diǎn)分別位于Hash環(huán)上的三個(gè)位置绸罗,然后Key值根據(jù)其HashCode意推,在Hash環(huán)上有一個(gè)固定位置,位置固定下之后珊蟀,Key就會(huì)順時(shí)針去尋找離它最近的一個(gè)Node菊值,把數(shù)據(jù)存儲(chǔ)在這個(gè)Node的MemCache服務(wù)器中。使用Hash環(huán)如果加了一個(gè)節(jié)點(diǎn)會(huì)怎么樣,看一下:
看到我加了一個(gè)Node4節(jié)點(diǎn)腻窒,只影響到了一個(gè)Key值的數(shù)據(jù)昵宇,本來(lái)這個(gè)Key值應(yīng)該是在Node1服務(wù)器上的,現(xiàn)在要去Node4了儿子。采用一致性Hash算法瓦哎,的確也會(huì)影響到整個(gè)集群,但是影響的只是加粗的那一段而已柔逼,相比余數(shù)Hash算法影響了遠(yuǎn)超一半的影響率蒋譬,這種影響要小得多。更重要的是愉适,集群中緩存服務(wù)器節(jié)點(diǎn)越多羡铲,增加節(jié)點(diǎn)帶來(lái)的影響越小,很好理解儡毕。換句話說(shuō)也切,隨著集群規(guī)模的增大,繼續(xù)命中原有緩存數(shù)據(jù)的概率會(huì)越來(lái)越大腰湾,雖然仍然有小部分?jǐn)?shù)據(jù)緩存在服務(wù)器中不能被讀到雷恃,但是這個(gè)比例足夠小,即使訪問(wèn)數(shù)據(jù)庫(kù)费坊,也不會(huì)對(duì)數(shù)據(jù)庫(kù)造成致命的負(fù)載壓力倒槐。
至于具體應(yīng)用,這個(gè)長(zhǎng)度為232的一致性Hash環(huán)通常使用二叉查找樹(shù)實(shí)現(xiàn)附井,至于二叉查找樹(shù)讨越,就是算法的問(wèn)題了,可以自己去查詢(xún)相關(guān)資料永毅。
MemCache是什么
MemCache是一個(gè)自由把跨、源碼開(kāi)放、高性能沼死、分布式的分布式內(nèi)存對(duì)象緩存系統(tǒng)着逐,用于動(dòng)態(tài)Web應(yīng)用以減輕數(shù)據(jù)庫(kù)的負(fù)載。它通過(guò)在內(nèi)存中緩存數(shù)據(jù)和對(duì)象來(lái)減少讀取數(shù)據(jù)庫(kù)的次數(shù)意蛀,從而提高了網(wǎng)站訪問(wèn)的速度耸别。MemCaChe是一個(gè)存儲(chǔ)鍵值對(duì)的HashMap,在內(nèi)存中對(duì)任意的數(shù)據(jù)(比如字符串县钥、對(duì)象等)所使用的key-value存儲(chǔ)秀姐,數(shù)據(jù)可以來(lái)自數(shù)據(jù)庫(kù)調(diào)用、API調(diào)用若贮,或者頁(yè)面渲染的結(jié)果省有。MemCache設(shè)計(jì)理念就是小而強(qiáng)大痒留,它簡(jiǎn)單的設(shè)計(jì)促進(jìn)了快速部署、易于開(kāi)發(fā)并解決面對(duì)大規(guī)模的數(shù)據(jù)緩存的許多難題锥咸,而所開(kāi)放的API使得MemCache能用于Java、C/C++/C#细移、Perl搏予、Python、PHP弧轧、Ruby等大部分流行的程序語(yǔ)言雪侥。
另外,說(shuō)一下MemCache和MemCached的區(qū)別:
1精绎、MemCache是項(xiàng)目的名稱(chēng)
2速缨、MemCached是MemCache服務(wù)器端可以執(zhí)行文件的名稱(chēng)
MemCache的官方網(wǎng)站為http://memcached.org/
MemCache訪問(wèn)模型
為了加深理解,我模仿著原阿里技術(shù)專(zhuān)家李智慧老師《大型網(wǎng)站技術(shù)架構(gòu) 核心原理與案例分析》一書(shū)MemCache部分代乃,自己畫(huà)了一張圖:
特別澄清一個(gè)問(wèn)題旬牲,MemCache雖然被稱(chēng)為"分布式緩存",但是MemCache本身完全不具備分布式的功能搁吓,MemCache集群之間不會(huì)相互通信(與之形成對(duì)比的原茅,比如JBoss Cache,某臺(tái)服務(wù)器有緩存數(shù)據(jù)更新時(shí)堕仔,會(huì)通知集群中其他機(jī)器更新緩存或清除緩存數(shù)據(jù))擂橘,所謂的"分布式",完全依賴(lài)于客戶端程序的實(shí)現(xiàn)摩骨,就像上面這張圖的流程一樣通贞。
同時(shí)基于這張圖,理一下MemCache一次寫(xiě)緩存的流程:
1恼五、應(yīng)用程序輸入需要寫(xiě)緩存的數(shù)據(jù)
2昌罩、API將Key輸入路由算法模塊,路由算法根據(jù)Key和MemCache集群服務(wù)器列表得到一臺(tái)服務(wù)器編號(hào)
3灾馒、由服務(wù)器編號(hào)得到MemCache及其的ip地址和端口號(hào)
4峡迷、API調(diào)用通信模塊和指定編號(hào)的服務(wù)器通信,將數(shù)據(jù)寫(xiě)入該服務(wù)器你虹,完成一次分布式緩存的寫(xiě)操作
讀緩存和寫(xiě)緩存一樣绘搞,只要使用相同的路由算法和服務(wù)器列表,只要應(yīng)用程序查詢(xún)的是相同的Key傅物,MemCache客戶端總是訪問(wèn)相同的客戶端去讀取數(shù)據(jù)夯辖,只要服務(wù)器中還緩存著該數(shù)據(jù),就能保證緩存命中董饰。
這種MemCache集群的方式也是從分區(qū)容錯(cuò)性的方面考慮的蒿褂,假如Node2宕機(jī)了圆米,那么Node2上面存儲(chǔ)的數(shù)據(jù)都不可用了,此時(shí)由于集群中Node0和Node1還存在啄栓,下一次請(qǐng)求Node2中存儲(chǔ)的Key值的時(shí)候娄帖,肯定是沒(méi)有命中的,這時(shí)先從數(shù)據(jù)庫(kù)中拿到要緩存的數(shù)據(jù)昙楚,然后路由算法模塊根據(jù)Key值在Node0和Node1中選取一個(gè)節(jié)點(diǎn)近速,把對(duì)應(yīng)的數(shù)據(jù)放進(jìn)去,這樣下一次就又可以走緩存了堪旧,這種集群的做法很好削葱,但是缺點(diǎn)是成本比較大。
MemCache實(shí)現(xiàn)原理
首先要說(shuō)明一點(diǎn)淳梦,MemCache的數(shù)據(jù)存放在內(nèi)存中析砸,存放在內(nèi)存中個(gè)人認(rèn)為意味著幾點(diǎn):
1、訪問(wèn)數(shù)據(jù)的速度比傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)要快爆袍,因?yàn)镺racle首繁、MySQL這些傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)為了保持?jǐn)?shù)據(jù)的持久性,數(shù)據(jù)存放在硬盤(pán)中陨囊,IO操作速度慢
2蛮瞄、MemCache的數(shù)據(jù)存放在內(nèi)存中同時(shí)意味著只要MemCache重啟了,數(shù)據(jù)就會(huì)消失
3谆扎、既然MemCache的數(shù)據(jù)存放在內(nèi)存中挂捅,那么勢(shì)必受到機(jī)器位數(shù)的限制,這個(gè)之前的文章寫(xiě)過(guò)很多次了堂湖,32位機(jī)器最多只能使用2GB的內(nèi)存空間闲先,64位機(jī)器可以認(rèn)為沒(méi)有上限
然后我們來(lái)看一下MemCache的原理,MemCache最重要的莫不是內(nèi)存分配的內(nèi)容了无蜂,MemCache采用的內(nèi)存分配方式是固定空間分配伺糠,還是自己畫(huà)一張圖說(shuō)明:
這張圖片里面涉及了slab_class、slab斥季、page训桶、chunk四個(gè)概念,它們之間的關(guān)系是:
1酣倾、MemCache將內(nèi)存空間分為一組slab
2舵揭、每個(gè)slab下又有若干個(gè)page,每個(gè)page默認(rèn)是1M躁锡,如果一個(gè)slab占用100M內(nèi)存的話午绳,那么這個(gè)slab下應(yīng)該有100個(gè)page
3、每個(gè)page里面包含一組chunk映之,chunk是真正存放數(shù)據(jù)的地方拦焚,同一個(gè)slab里面的chunk的大小是固定的
4蜡坊、有相同大小chunk的slab被組織在一起,稱(chēng)為slab_class
MemCache內(nèi)存分配的方式稱(chēng)為allocator赎败,slab的數(shù)量是有限的秕衙,幾個(gè)、十幾個(gè)或者幾十個(gè)僵刮,這個(gè)和啟動(dòng)參數(shù)的配置相關(guān)据忘。
MemCache中的value過(guò)來(lái)存放的地方是由value的大小決定的,value總是會(huì)被存放到與chunk大小最接近的一個(gè)slab中妓笙,比如slab[1]的chunk大小為80字節(jié)若河、slab[2]的chunk大小為100字節(jié)能岩、slab[3]的chunk大小為128字節(jié)(相鄰slab內(nèi)的chunk基本以1.25為比例進(jìn)行增長(zhǎng)寞宫,MemCache啟動(dòng)時(shí)可以用-f指定這個(gè)比例),那么過(guò)來(lái)一個(gè)88字節(jié)的value拉鹃,這個(gè)value將被放到2號(hào)slab中辈赋。放slab的時(shí)候,首先slab要申請(qǐng)內(nèi)存膏燕,申請(qǐng)內(nèi)存是以page為單位的钥屈,所以在放入第一個(gè)數(shù)據(jù)的時(shí)候,無(wú)論大小為多少坝辫,都會(huì)有1M大小的page被分配給該slab篷就。申請(qǐng)到page后,slab會(huì)將這個(gè)page的內(nèi)存按chunk的大小進(jìn)行切分近忙,這樣就變成了一個(gè)chunk數(shù)組竭业,最后從這個(gè)chunk數(shù)組中選擇一個(gè)用于存儲(chǔ)數(shù)據(jù)。
如果這個(gè)slab中沒(méi)有chunk可以分配了怎么辦及舍,如果MemCache啟動(dòng)沒(méi)有追加-M(禁止LRU未辆,這種情況下內(nèi)存不夠會(huì)報(bào)Out Of Memory錯(cuò)誤),那么MemCache會(huì)把這個(gè)slab中最近最少使用的chunk中的數(shù)據(jù)清理掉锯玛,然后放上最新的數(shù)據(jù)咐柜。針對(duì)MemCache的內(nèi)存分配及回收算法,總結(jié)三點(diǎn):
1攘残、MemCache的內(nèi)存分配chunk里面會(huì)有內(nèi)存浪費(fèi)拙友,88字節(jié)的value分配在128字節(jié)(緊接著大的用)的chunk中,就損失了30字節(jié)歼郭,但是這也避免了管理內(nèi)存碎片的問(wèn)題
2献宫、MemCache的LRU算法不是針對(duì)全局的,是針對(duì)slab的
3实撒、應(yīng)該可以理解為什么MemCache存放的value大小是限制的姊途,因?yàn)橐粋€(gè)新數(shù)據(jù)過(guò)來(lái)涉瘾,slab會(huì)先以page為單位申請(qǐng)一塊內(nèi)存,申請(qǐng)的內(nèi)存最多就只有1M捷兰,所以value大小自然不能大于1M了
再總結(jié)MemCache的特性和限制
上面已經(jīng)對(duì)于MemCache做了一個(gè)比較詳細(xì)的解讀立叛,這里再次總結(jié)MemCache的限制和特性:
1、MemCache中可以保存的item數(shù)據(jù)量是沒(méi)有限制的贡茅,只要內(nèi)存足夠
2秘蛇、MemCache單進(jìn)程在32位機(jī)中最大使用內(nèi)存為2G,這個(gè)之前的文章提了多次了顶考,64位機(jī)則沒(méi)有限制
3赁还、Key最大為250個(gè)字節(jié),超過(guò)該長(zhǎng)度無(wú)法存儲(chǔ)
4驹沿、單個(gè)item最大數(shù)據(jù)是1MB艘策,超過(guò)1MB的數(shù)據(jù)不予存儲(chǔ)
5、MemCache服務(wù)端是不安全的渊季,比如已知某個(gè)MemCache節(jié)點(diǎn)朋蔫,可以直接telnet過(guò)去,并通過(guò)flush_all讓已經(jīng)存在的鍵值對(duì)立即失效
6却汉、不能夠遍歷MemCache中所有的item驯妄,因?yàn)檫@個(gè)操作的速度相對(duì)緩慢且會(huì)阻塞其他的操作
7、MemCache的高性能源自于兩階段哈希結(jié)構(gòu):第一階段在客戶端合砂,通過(guò)Hash算法根據(jù)Key值算出一個(gè)節(jié)點(diǎn)青扔;第二階段在服務(wù)端,通過(guò)一個(gè)內(nèi)部的Hash算法翩伪,查找真正的item并返回給客戶端微猖。從實(shí)現(xiàn)的角度看,MemCache是一個(gè)非阻塞的幻工、基于事件的服務(wù)器程序
8励两、MemCache設(shè)置添加某一個(gè)Key值的時(shí)候,傳入expiry為0表示這個(gè)Key值永久有效囊颅,這個(gè)Key值也會(huì)在30天之后失效当悔,見(jiàn)memcache.c的源代碼:
#define REALTIME_MAXDELTA 60*60*24*30staticrel_time_t realtime(consttime_t exptime) {if(exptime == 0)return0;if(exptime >REALTIME_MAXDELTA) {if(exptime <=process_started)return(rel_time_t)1;return(rel_time_t)(exptime -process_started);
}else{return(rel_time_t)(exptime +current_time);
}
}
這個(gè)失效的時(shí)間是memcache源碼里面寫(xiě)的,開(kāi)發(fā)者沒(méi)有辦法改變MemCache的Key值失效時(shí)間為30天這個(gè)限制
MemCache指令匯總
上面說(shuō)過(guò)踢代,已知MemCache的某個(gè)節(jié)點(diǎn)盲憎,直接telnet過(guò)去,就可以使用各種命令操作MemCache了胳挎,下面看下MemCache有哪幾種命令:
命 ? ?令作 ? ?用
get返回Key對(duì)應(yīng)的Value值
add添加一個(gè)Key值饼疙,沒(méi)有則添加成功并提示STORED,有則失敗并提示NOT_STORED
set無(wú)條件地設(shè)置一個(gè)Key值慕爬,沒(méi)有就增加窑眯,有就覆蓋屏积,操作成功提示STORED
replace按照相應(yīng)的Key值替換數(shù)據(jù),如果Key值不存在則會(huì)操作失敗
stats返回MemCache通用統(tǒng)計(jì)信息(下面有詳細(xì)解讀)
stats items返回各個(gè)slab中item的數(shù)目和最老的item的年齡(最后一次訪問(wèn)距離現(xiàn)在的秒數(shù))
stats slabs返回MemCache運(yùn)行期間創(chuàng)建的每個(gè)slab的信息(下面有詳細(xì)解讀)
version返回當(dāng)前MemCache版本號(hào)
flush_all清空所有鍵值磅甩,但不會(huì)刪除items炊林,所以此時(shí)MemCache依舊占用內(nèi)存
quit關(guān)閉連接
stats指令解讀
stats是一個(gè)比較重要的指令,用于列出當(dāng)前MemCache服務(wù)器的狀態(tài)卷要,拿一組數(shù)據(jù)舉個(gè)例子:
STAT pid 1023STAT uptime21069937STAT time1447235954STAT version1.4.5STAT pointer_size64STAT rusage_user1167.020934STAT rusage_system3346.933170STAT curr_connections29STAT total_connections21STAT connection_structures49STAT cmd_get49STAT cmd_set7458STAT cmd_flush0STAT get_hits7401STAT get_misses57..(delete渣聚、incr、decr僧叉、cas的hits和misses數(shù)奕枝,cas還多一個(gè)badval)
STAT auth_cmds0STAT auth_errors0STAT bytes_read22026555STAT bytes_written8930466STAT limit_maxbytes4134304000STAT accepting_conns1STAT listen_disabled_num0STAT threads4STAT bytes151255336STAT current_items57146STAT total_items580656STAT evicitions0
這些參數(shù)反映著MemCache服務(wù)器的基本信息,它們的意思是:
參 ?數(shù) ?名作 ? ? ?用
pidMemCache服務(wù)器的進(jìn)程id
uptime服務(wù)器已經(jīng)運(yùn)行的秒數(shù)
time服務(wù)器當(dāng)前的UNIX時(shí)間戳
versionMemCache版本
pointer_size當(dāng)前操作系統(tǒng)指針大小瓶堕,反映了操作系統(tǒng)的位數(shù)隘道,64意味著MemCache服務(wù)器是64位的
rusage_user進(jìn)程的累計(jì)用戶時(shí)間
rusage_system進(jìn)程的累計(jì)系統(tǒng)時(shí)間
curr_connections當(dāng)前打開(kāi)著的連接數(shù)
total_connections當(dāng)服務(wù)器啟動(dòng)以后曾經(jīng)打開(kāi)過(guò)的連接數(shù)
connection_structures服務(wù)器分配的連接構(gòu)造數(shù)
cmd_getget命令總請(qǐng)求次數(shù)
cmd_setset命令總請(qǐng)求次數(shù)
cmd_flushflush_all命令總請(qǐng)求次數(shù)
get_hits總命中次數(shù),重要捞烟,緩存最重要的參數(shù)就是緩存命中率薄声,以get_hits / (get_hits + get_misses)表示当船,比如這個(gè)緩存命中率就是99.2%
get_misses總未命中次數(shù)
auth_cmds認(rèn)證命令的處理次數(shù)
auth_errors認(rèn)證失敗的處理次數(shù)
bytes_read總讀取的字節(jié)數(shù)
bytes_written總發(fā)送的字節(jié)數(shù)
limit_maxbytes分配給MemCache的內(nèi)存大刑饣(單位為字節(jié))
accepting_conns是否已經(jīng)達(dá)到連接的最大值,1表示達(dá)到德频,0表示未達(dá)到
listen_disabled_num統(tǒng)計(jì)當(dāng)前服務(wù)器連接數(shù)曾經(jīng)達(dá)到最大連接的次數(shù)苍息,這個(gè)次數(shù)應(yīng)該為0或者接近于0,如果這個(gè)數(shù)字不斷增長(zhǎng)壹置, 就要小心我們的服務(wù)了
threads當(dāng)前MemCache總線程數(shù)竞思,由于MemCache的線程是基于事件驅(qū)動(dòng)機(jī)制的,因此不會(huì)一個(gè)線程對(duì)應(yīng)一個(gè)用戶請(qǐng)求
bytes當(dāng)前服務(wù)器存儲(chǔ)的items總字節(jié)數(shù)
current_items當(dāng)前服務(wù)器存儲(chǔ)的items總數(shù)量
total_items自服務(wù)器啟動(dòng)以后存儲(chǔ)的items總數(shù)量
stats slab指令解讀
如果對(duì)上面的MemCache存儲(chǔ)機(jī)制比較理解了钞护,那么我們來(lái)看一下各個(gè)slab中的信息盖喷,還是拿一組數(shù)據(jù)舉個(gè)例子:
1STAT1:chunk_size 962...3STAT 2:chunk_size 1444STAT 2:chunks_per_page 72815STAT 2:total_pages 76STAT 2:total_chunks 509677STAT 2:used_chunks 451978STAT 2:free_chunks 19STAT 2:free_chunks_end 576910STAT 2:mem_requested 608463811STAT 2:get_hits 4808412STAT 2:cmd_set 5958827113STAT 2:delete_hits 014STAT 2:incr_hits 015STAT 2:decr_hits 016STAT 2:cas_hits 017STAT 2:cas_badval 018...19STAT 3:chunk_size 21620...
首先看到,第二個(gè)slab的chunk_size(144)/第一個(gè)slab的chunk_size(96)=1.5难咕,第三個(gè)slab的chunk_size(216)/第二個(gè)slab的chunk_size(144)=1.5课梳,可以確定這個(gè)MemCache的增長(zhǎng)因子是1.5,chunk_size以1.5倍增長(zhǎng)余佃。然后解釋下字段的含義:
參 ?數(shù) ?名作 ? ? ?用
chunk_size當(dāng)前slab每個(gè)chunk的大小暮刃,單位為字節(jié)
chunks_per_page每個(gè)page可以存放的chunk數(shù)目,由于每個(gè)page固定為1M即1024*1024字節(jié)爆土,所以這個(gè)值就是(1024*1024/chunk_size)
total_pages分配給當(dāng)前slab的page總數(shù)
total_chunks當(dāng)前slab最多能夠存放的chunk數(shù)椭懊,這個(gè)值是total_pages*chunks_per_page
used_chunks已經(jīng)被分配給存儲(chǔ)對(duì)象的chunks數(shù)目
free_chunks曾經(jīng)被使用過(guò)但是因?yàn)檫^(guò)期而被回收的chunk數(shù)
free_chunks_end新分配但還沒(méi)有被使用的chunk數(shù),這個(gè)值不為0則說(shuō)明當(dāng)前slab從來(lái)沒(méi)有出現(xiàn)過(guò)容量不夠的時(shí)候
mem_requested當(dāng)前slab中被請(qǐng)求用來(lái)存儲(chǔ)數(shù)據(jù)的內(nèi)存空間字節(jié)總數(shù)步势,(total_chunks*chunk_size)-mem_requested表示有多少內(nèi)存在當(dāng)前slab中是被閑置的氧猬,這包括未用的slab+使用的slab中浪費(fèi)的內(nèi)存
get_hits當(dāng)前slab中命中的get請(qǐng)求數(shù)
cmd_set當(dāng)前slab中接收的所有set命令請(qǐng)求數(shù)
delete_hits當(dāng)前slab中命中的delete請(qǐng)求數(shù)
incr_hits當(dāng)前slab中命中的incr請(qǐng)求數(shù)
decr_hits當(dāng)前slab中命中的decr請(qǐng)求數(shù)
cas_hits當(dāng)前slab中命中的cas請(qǐng)求數(shù)
cas_badval當(dāng)前slab中命中但是更新失敗的cas請(qǐng)求數(shù)
看到這個(gè)命令的輸出量很大背犯,所有信息都很有作用。舉個(gè)例子吧盅抚,比如第一個(gè)slab中使用的chunks很少媳板,第二個(gè)slab中使用的chunks很多,這時(shí)就可以考慮適當(dāng)增大MemCache的增長(zhǎng)因子了泉哈,讓一部分?jǐn)?shù)據(jù)落到第一個(gè)slab中去蛉幸,適當(dāng)平衡兩個(gè)slab中的內(nèi)存,避免空間浪費(fèi)丛晦。
MemCache的Java實(shí)現(xiàn)實(shí)例
講了這么多奕纫,作為一個(gè)Java程序員,怎么能不寫(xiě)寫(xiě)MemCache的客戶端的實(shí)現(xiàn)呢烫沙?MemCache的客戶端有很多第三方j(luò)ar包提供了實(shí)現(xiàn)匹层,其中比較好的當(dāng)屬XMemCached了,XMemCached具有效率高锌蓄、IO非阻塞升筏、資源耗費(fèi)少、支持完整的協(xié)議瘸爽、允許設(shè)置節(jié)點(diǎn)權(quán)重您访、允許動(dòng)態(tài)增刪節(jié)點(diǎn)、支持JMX剪决、支持與Spring框架集成灵汪、使用連接池、可擴(kuò)展性好等諸多優(yōu)點(diǎn)柑潦,因而被廣泛使用享言。這里利用XMemCache寫(xiě)一個(gè)簡(jiǎn)單的MemCache客戶單實(shí)例,也沒(méi)有驗(yàn)證過(guò)渗鬼,純屬拋磚引玉:
publicclassMemCacheManager
{privatestaticMemCacheManager instance =newMemCacheManager();/**XMemCache允許開(kāi)發(fā)者通過(guò)設(shè)置節(jié)點(diǎn)權(quán)重來(lái)調(diào)節(jié)MemCache的負(fù)載览露,設(shè)置的權(quán)重越高,該MemCache節(jié)點(diǎn)存儲(chǔ)的數(shù)據(jù)越多譬胎,負(fù)載越大*/privatestaticMemcachedClientBuilder mcb =newXMemcachedClientBuilder(AddrUtil.getAddresses("127.0.0.1:11211 127.0.0.2:11211 127.0.0.3:11211"),newint[]{1, 3, 5});privatestaticMemcachedClient mc =null;/**初始化加載客戶端MemCache信息*/static{
mcb.setCommandFactory(newBinaryCommandFactory());//使用二進(jìn)制文件mcb.setConnectionPoolSize(10);//連接池個(gè)數(shù)差牛,即客戶端個(gè)數(shù)try{
mc=mcb.build();
}catch(IOException e)
{
e.printStackTrace();
}
}privateMemCacheManager()
{
}publicMemCacheManager getInstance()
{returninstance;
}/**向MemCache服務(wù)器設(shè)置數(shù)據(jù)*/publicvoidset(String key,intexpiry, Object obj)throwsException
{
mc.set(key, expiry, obj);
}/**從MemCache服務(wù)器獲取數(shù)據(jù)*/publicObject get(String key)throwsException
{returnmc.get(key);
}/*** MemCache通過(guò)compare and set即cas協(xié)議實(shí)現(xiàn)原子更新,類(lèi)似樂(lè)觀鎖银择,每次請(qǐng)求存儲(chǔ)某個(gè)數(shù)據(jù)都要附帶一個(gè)cas值多糠,MemCache
* 比對(duì)這個(gè)cas值與當(dāng)前存儲(chǔ)數(shù)據(jù)的cas值是否相等,如果相等就覆蓋老數(shù)據(jù)浩考,如果不相等就認(rèn)為更新失敗夹孔,這在并發(fā)環(huán)境下特別有用*/publicbooleanupdate(String key, Integer i)throwsException
{
GetsResponse result =mc.gets(key);longcas =result.getCas();//嘗試更新key對(duì)應(yīng)的valueif(!mc.cas(key, 0, i, cas))
{returnfalse;
}returntrue;
}
}