memcache的原理和命中率的总结
Memcache是danga.com的一個(gè)項(xiàng)目,最早是為 LiveJournal 服務(wù)的,目前全世界不少人使用這個(gè)緩存項(xiàng)目來構(gòu)建自己大負(fù)載的網(wǎng)站,來分擔(dān)數(shù)據(jù)庫的壓力。
它可以應(yīng)對任意多個(gè)連接,使用非阻塞的網(wǎng)絡(luò)IO。由于它的工作機(jī)制是在內(nèi)存中開辟一塊空間,然后建立一個(gè)HashTable,Memcached自治理這些HashTable。
???
為什么會有Memcache和memcached兩種名稱?
實(shí)在Memcache是這個(gè)項(xiàng)目的名稱,而memcached是它服務(wù)器真?zhèn)€主程序文件名,
????
Memcache官方網(wǎng)站:http://www.danga.com/memcached,
2?????? Memcache工作原理
首先 memcached 是以守護(hù)程序方式運(yùn)行于一個(gè)或多個(gè)服務(wù)器中,隨時(shí)接受客戶真?zhèn)€連接操縱,客戶端可以由各種語言編寫,目前已知的客戶端 API 包括 Perl/PHP/Python/Ruby/Java/C#/C 等等。客戶端在與 memcached 服務(wù)建立連接之后,接下來的事情就是存取對象了,每個(gè)被存取的對象都有一個(gè)唯一的標(biāo)識符 key,存取操縱均通過這個(gè) key 進(jìn)行,保存到 memcached 中的對象實(shí)際上是放置內(nèi)存中的,并不是保存在 cache 文件中的,這也是為什么 memcached 能夠如此高效快速的原因。留意,這些對象并不是持久的,服務(wù)停止之后,里邊的數(shù)據(jù)就會丟失。
與很多 cache 工具類似,Memcached 的原理并不復(fù)雜。它采用了C/S的模式,在 server 端啟動服務(wù)進(jìn)程,在啟動時(shí)可以指定監(jiān)聽的 ip,自己的端口號,所使用的內(nèi)存大小等幾個(gè)關(guān)鍵參數(shù)。一旦啟動,服務(wù)就一直處于可用狀態(tài)。Memcached 的目前版本是通過C實(shí)現(xiàn),采用了單進(jìn)程,單線程,異步I/O,基于事件 (event_based) 的服務(wù)方式.使用 libevent 作為事件通知實(shí)現(xiàn)。多個(gè) Server 可以協(xié)同工作,但這些 Server 之間是沒有任何通訊聯(lián)系的,每個(gè) Server 只是對自己的數(shù)據(jù)進(jìn)行治理。Client 端通過指定 Server 真?zhèn)€ ip 地址(通過域名應(yīng)該也可以)。需要緩存的對象或數(shù)據(jù)是以 key->value 對的形式保存在Server端。key 的值通過 hash 進(jìn)行轉(zhuǎn)換,根據(jù) hash 值把 value 傳遞到對應(yīng)的具體的某個(gè) Server 上。當(dāng)需要獲取對象數(shù)據(jù)時(shí),也根據(jù) key 進(jìn)行。首先對 key 進(jìn)行 hash,通過獲得的值可以確定它被保存在了哪臺 Server 上,然后再向該 Server 發(fā)出請求。Client 端只需要知道保存 hash(key) 的值在哪臺服務(wù)器上就可以了。
??????? 實(shí)在說到底,memcache 的工作就是在專門的機(jī)器的內(nèi)存里維護(hù)一張巨大的 hash 表,來存儲經(jīng)常被讀寫的一些數(shù)組與文件,從而極大的進(jìn)步網(wǎng)站的運(yùn)行效率。
memcache 命中率
首先查看,
在linux或window上有telnet連接memcache,然后輸入stats會出現(xiàn)下面的命令,這些狀態(tài)的說明如下:
pid
memcache服務(wù)器的進(jìn)程ID
uptime
服務(wù)器已經(jīng)運(yùn)行的秒數(shù)
time
服務(wù)器當(dāng)前的unix時(shí)間戳
version
memcache版本
pointer_size
當(dāng)前操作系統(tǒng)的指針大小(32位系統(tǒng)一般是32bit)
rusage_user
進(jìn)程的累計(jì)用戶時(shí)間
rusage_system
進(jìn)程的累計(jì)系統(tǒng)時(shí)間
curr_items
服務(wù)器當(dāng)前存儲的items數(shù)量
total_items
從服務(wù)器啟動以后存儲的items總數(shù)量
bytes
當(dāng)前服務(wù)器存儲items占用的字節(jié)數(shù)
curr_connections
當(dāng)前打開著的連接數(shù)
total_connections
從服務(wù)器啟動以后曾經(jīng)打開過的連接數(shù)
connection_structures
服務(wù)器分配的連接構(gòu)造數(shù)
cmd_get
get命令(獲取)總請求次數(shù)
cmd_set
set命令(保存)總請求次數(shù)
get_hits
總命中次數(shù)
get_misses
總未命中次數(shù)
evictions
為獲取空閑內(nèi)存而刪除的items數(shù)(分配給memcache的空間用滿后需要刪除舊的items來得到空間分配給新的items)
bytes_read
總讀取字節(jié)數(shù)(請求字節(jié)數(shù))
bytes_written
總發(fā)送字節(jié)數(shù)(結(jié)果字節(jié)數(shù))
limit_maxbytes
分配給memcache的內(nèi)存大小(字節(jié))
threads
當(dāng)前線程數(shù)
一、緩存命中率 = get_hits/cmd_get * 100%
二、get_misses的數(shù)字加上get_hits應(yīng)該等于cmd_get
三、total_items == cmd_set == get_misses,當(dāng)可用最大內(nèi)存用光時(shí),memcached就會刪掉一些內(nèi)容,等式就會不成立
Memcached,人所皆知的remote distribute cache(不知道的可以javaeye一下下,或者google一下下,或者baidu一下下,但是鑒于baidu的排名商業(yè)味道太濃(從最近得某某事件可以看出),所以還是建議javaeye一下下),使用起來也非常的簡單,它被用在了很多網(wǎng)站上面,幾乎很少有大型的網(wǎng)站不會使用memcached。?
曾經(jīng)我也看過很多剖析memcached內(nèi)部機(jī)制的文章,有一點(diǎn)收獲,但是看過之后又忘記了,而且沒有什么深刻的概念,但是最近我遇到一個(gè)問題,這個(gè)問題迫使我重新來認(rèn)識memcache,下面我闡述一下我遇到的問題?
問題:我有幾千萬的數(shù)據(jù),這些數(shù)據(jù)會經(jīng)常被用到,目前來看,它必須要放到memcached中,以保證訪問速度,但是我的memcached中數(shù)據(jù)經(jīng)常會有丟失,而業(yè)務(wù)需求是memcached中的數(shù)據(jù)是不能丟失的。我的數(shù)據(jù)丟失的時(shí)候,memcached server的內(nèi)存才使用到60%,也就是還有40%內(nèi)存被嚴(yán)重的浪費(fèi)掉了。但不是所有的應(yīng)用都是這樣,其他應(yīng)用內(nèi)存浪費(fèi)的就比較少。為什么內(nèi)存才使用到60%的時(shí)候LRU就執(zhí)行了呢(之所以確定是LRU執(zhí)行是因?yàn)槲野l(fā)現(xiàn)我的數(shù)據(jù)丟失的總是前面放進(jìn)去的,而且這個(gè)過程中,這些數(shù)據(jù)都沒有被訪問,比如第一次訪問的時(shí)候,只能訪問第1000w條,而第300w條或者之前的數(shù)據(jù)都已經(jīng)丟失了,從日志里看,第300w條肯定是放進(jìn)去了)。?
帶著這些疑問,我開始重新審視memcached這個(gè)產(chǎn)品,首先從它的內(nèi)存模型開始:我們知道c++里分配內(nèi)存有兩種方式,預(yù)先分配和動態(tài)分配,顯然,預(yù)先分配內(nèi)存會使程序比較快,但是它的缺點(diǎn)是不能有效利用內(nèi)存,而動態(tài)分配可以有效利用內(nèi)存,但是會使程序運(yùn)行效率下降,memcached的內(nèi)存分配就是基于以上原理,顯然為了獲得更快的速度,有時(shí)候我們不得不以空間換時(shí)間。?
也就是說memcached會預(yù)先分配內(nèi)存,對了,memcached分配內(nèi)存方式稱之為allocator,首先,這里有3個(gè)概念:?
1 slab?
2 page?
3 chunk?
解釋一下,一般來說一個(gè)memcahced進(jìn)程會預(yù)先將自己劃分為若干個(gè)slab,每個(gè)slab下又有若干個(gè)page,每個(gè)page下又有多個(gè)chunk,如果我們把這3個(gè)咚咚看作是object得話,這是兩個(gè)一對多得關(guān)系。再一般來說,slab得數(shù)量是有限得,幾個(gè),十幾個(gè),或者幾十個(gè),這個(gè)跟進(jìn)程配置得內(nèi)存有關(guān)。而每個(gè)slab下得page默認(rèn)情況是1m,也就是說如果一個(gè)slab占用100m得內(nèi)存得話,那么默認(rèn)情況下這個(gè)slab所擁有得page得個(gè)數(shù)就是100,而chunk就是我們得數(shù)據(jù)存放得最終地方。?
舉一個(gè)例子,我啟動一個(gè)memcached進(jìn)程,占用內(nèi)存100m,再打開telnet,telnet localhost 11211,連接上memcache之后,輸入stats slabs,回車,出現(xiàn)如下數(shù)據(jù):?
chunk_size表示數(shù)據(jù)存放塊得大小,chunks_per_page表示一個(gè)內(nèi)存頁page中擁有得chunk得數(shù)量,total_pages表示每個(gè)slab下page得個(gè)數(shù)。total_chunks表示這個(gè)slab下chunk得總數(shù)(=total_pages * chunks_per_page),used_chunks表示該slab下已經(jīng)使用得chunk得數(shù)量,free_chunks表示該slab下還可以使用得chunks數(shù)量。?
從上面得示例slab 1一共有1m得內(nèi)存空間,而且現(xiàn)在已經(jīng)被用完了,slab2也有1m得內(nèi)存空間,也被用完了,slab3得情況依然如此。 而且從這3個(gè)slab中chunk得size可以看出來,第一個(gè)chunk為80b,第二個(gè)是100b,第3個(gè)是128b,基本上后一個(gè)是前一個(gè)得1.25倍,但是這個(gè)增長情況我們是可以控制得,我們可以通過在啟動時(shí)得進(jìn)程參數(shù) –f來修改這個(gè)值,比如說 –f 1.1表示這個(gè)增長因子為1.1,那么第一個(gè)slab中得chunk為80b得話,第二個(gè)slab中得chunk應(yīng)該是80*1.1左右。?
解釋了這么多也該可以看出來我遇到得問題得原因了,如果還看不出來,那我再補(bǔ)充關(guān)鍵的一句:memcached中新的value過來存放的地址是該value的大小決定的,value總是會被選擇存放到chunk與其最接近的一個(gè)slab中,比如上面的例子,如果我的value是80b,那么我這所有的value總是會被存放到1號slab中,而1號slab中的free_chunks已經(jīng)是0了,怎么辦呢,如果你在啟動memcached的時(shí)候沒有追加-M(禁止LRU,這種情況下內(nèi)存不夠時(shí)會out of memory),那么memcached會把這個(gè)slab中最近最少被使用的chunk中的數(shù)據(jù)清掉,然后放上最新的數(shù)據(jù)。這就解釋了為什么我的內(nèi)存還有40%的時(shí)候LRU就執(zhí)行了,因?yàn)槲业钠渌鹲lab中的chunk_size都遠(yuǎn)大于我的value,所以我的value根本不會放到那幾個(gè)slab中,而只會放到和我的value最接近的chunk所在的slab中(而這些slab早就滿了,郁悶了)。這就導(dǎo)致了我的數(shù)據(jù)被不停的覆蓋,后者覆蓋前者。?
問題找到了,解決方案還是沒有找到,因?yàn)槲业臄?shù)據(jù)必須要求命中率時(shí)100%,我只能通過調(diào)整slab的增長因子和page的大小來盡量來使命中率接近100%,但是并不能100%保證命中率是100%(這話怎么讀起來這么別扭呢,自我檢討一下自己的語文水平),如果您說,這種方案不行啊,因?yàn)槲业膍emcached server不能停啊,不要緊還有另外一個(gè)方法,就是memcached-tool,執(zhí)行move命令,如:move 3 1,代表把3號slab中的一個(gè)內(nèi)存頁移動到1號slab中,有人問了,這有什么用呢,比如說我的20號slab的利用率非常低,但是page卻又很多,比如200,那么就是200m,而2好slab經(jīng)常發(fā)生LRU,明顯page不夠,我就可以move 20 2,把20號slab的一個(gè)內(nèi)存頁移動到2號slab上,這樣就能更加有效的利用內(nèi)存了(有人說了,一次只移動一個(gè)page,多麻煩啊?ahuaxuan說,還是寫個(gè)腳本,循環(huán)一下吧)。?
有人說不行啊,我的memcache中的數(shù)據(jù)不能丟失啊,ok,試試新浪的memcachedb吧,雖然我沒有用過,但是建議大家可以試試,它也使利用memcache協(xié)議和berkeleyDB做的(寫到這里,我不得不佩服danga了,我覺得它最大的貢獻(xiàn)不是memcache server本身,而是memcache協(xié)議),據(jù)說它被用在新浪的不少應(yīng)用上,包括新浪的博客。?
補(bǔ)充,stats slab命令可以查看memcached中slab的情況,而stats命令可以查看你的memcached的一些健康情況,比如說命中率之類的,示例如下:
從上面的數(shù)據(jù)可以看到這個(gè)memcached進(jìn)程的命中率很好,get_misses低達(dá)0個(gè),怎么回事啊,因?yàn)檫@個(gè)進(jìn)程使我剛啟動的,我只用telnet連了一下,所以curr_connections為1,而total_items為0,因?yàn)槲覜]有放數(shù)據(jù)進(jìn)去,get_hits為0,因?yàn)槲覜]有調(diào)用get方法,最后的結(jié)果就是misses當(dāng)然為0,哇哦,換句話說命中率就是100%,又yy了。?
該到總結(jié)的時(shí)候了,從這篇文章里我們可以得到以下幾個(gè)結(jié)論:?
結(jié)論一,memcached得LRU不是全局的,而是針對slab的,可以說是區(qū)域性的。?
結(jié)論二,要提高memcached的命中率,預(yù)估我們的value大小并且適當(dāng)?shù)恼{(diào)整內(nèi)存頁大小和增長因子是必須的。?
結(jié)論三,帶著問題找答案理解的要比隨便看看的效果好得多。
轉(zhuǎn)至:http://www.javaeye.com/topic/225692
來自:?http://hi.baidu.com/xkplt/blog/item/e3813a2ad2c62d345243c113.html
總結(jié)
以上是生活随笔為你收集整理的memcache的原理和命中率的总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ice笔记--C++线程与并发(小结)
- 下一篇: Memcache,Redis,Mongo