memcache的內存回收機制


memcache不會釋放內存,而是重新利用。

在緩存的清除方面,memcache是不釋放已分配內存。當已分配的內存所在的記錄失效后,這段以往的內存空間,memcache只會重復利用。

memcached的內存回收機制不是說你設置的key到了生命周期就自動從內存中清除的,這個時候必須有一個新的對象入駐請求這個大小的chunk或者 這個過期的對象被get的時候才會清除。

那當所有給memcache的內存都被占用了,這個時候,memcache有兩個設置,要么報錯,要么,就是用 LRU方法,把last recently used的數據清除出去,也就是刪除近段時間最少使用的同規格chunk。
 
這個時候就會引發應外一個問題,就是當你chunk大小設置不合理的時候,比如slab20 chunk大小非常大,一開始占用了很多內存,但是之后不論是否過期,不被再次利用到的時候就一直處於內存中,這樣,當比較小的slab1中的chunk 滿了,也沒有內存新建slab並分割和slab1同樣規格的chunk的時候,memcached就要啟動LRU,來清理這個slab下的數據。那這種情 況就會造成在內存的極大程度浪費和cache命中率下降,是某些關鍵性的數據總是在內存中進進出出,得不到持久的保存。
這個時候我們可以調整factor參數,讓chunk的大小達到我們的需要,這個需要根據業務來做。
那slab數量,growth factor大小,LRU頻率以及空間浪費情況可以估計出來如下結果:
growth factor↑ slab數量 ↓ LRU頻率↓ 空降浪費↑
growth factor↓ slab數量 ↑ LRU頻率↑ 空降浪費↓
 
關於LRU
我們知道c++里分配內存有兩種方式,預先分配和動態分配,顯然,預先分配內存會使程序比較快,但是它的缺點是不能有效利用內存,而動態分配可以有效利用 內存,但是會使程序運行效率下降,memcached的內存分配就是基於以上原理,顯然為了獲得更快的速度,有時候我們不得不以空間換時間。
我啟動一個memcached進程,占用內存100m,再打開telnet,telnet localhost 11211,連接上memcache之后, 輸入stats  slabs,回車,出現如下數據:
STAT 1:chunk_size 80
STAT 1:chunks_per_page 13107
STAT 1:total_pages 1
STAT 1:total_chunks 13107
STAT 1:used_chunks 13107
STAT 1:free_chunks 0
STAT 1:free_chunks_end 13107
STAT 2:chunk_size 100
STAT 2:chunks_per_page 10485
STAT 2:total_pages 1
STAT 2:total_chunks 10485
STAT 2:used_chunks 10485
STAT 2:free_chunks 0
STAT 2:free_chunks_end 10485
STAT 3:chunk_size 128
STAT 3:chunks_per_page 8192
STAT 3:total_pages 1
STAT 3:total_chunks 8192
STAT 3:used_chunks 8192
STAT 3:free_chunks 0
STAT 3:free_chunks_end 8192

以上就是前3個slab得詳細信息
chunk_size表示數據存放塊得大小,chunks_per_page表示一個內存頁page中擁有得chunk得數 量,total_pages表示每個slab下page得個數。total_chunks表示這個slab下chunk得總數(=total_pages * chunks_per_page),used_chunks表示該slab下已經使用得chunk得數量,free_chunks表示該slab下還可以 使用得chunks數量。

從上面得示例slab 1一共有1m得內存空間,而且現在已經被用完了,slab2也有1m得內存空間,也被用完了,slab3得情況依然如此。 而且從這3個slab中chunk得size可以看出來,第一個chunk為80b,第二個是100b,第3個是128b,基本上后一個是前一個得 1.25倍,但是這個增長情況我們是可以控制得,我們可以通過在啟動時得進程參數 –f來修改這個值,比如說 –f 1.1表示這個增長因子為1.1,那么第一個slab中得chunk為80b得話,第二個slab中得chunk應該是80*1.1左右。

解釋了這么多也該可以看出來我遇到得問題得原因了,如果還看不出來,那我再補充關鍵的一句:memcached中新的value過來存放的地址是 該value的大小決定的,value總是會被選擇存放到chunk與其最接近的一個slab中,比如上面的例子,如果我的value是80b,那么我這 所有的value總是會被存放到1號slab中,而1號slab中的free_chunks已經是0了,怎么辦呢,如果你在啟動memcached的時候 沒有追加-M(禁止LRU,這種情況下內存不夠時會out of memory),那么memcached會把這個slab中最近最少被使用的chunk中的數據清掉,然后放上最新的數據。這就解釋了為什么我的內存還有 40%的時候LRU就執行了,因為我的其他slab中的chunk_size都遠大於我的value,所以我的value根本不會放到那幾個slab中, 而只會放到和我的value最接近的chunk所在的slab中(而這些slab早就滿了,郁悶了)。這就導致了我的數據被不停的覆蓋,后者覆蓋前者。

問題找到了,解決方案還是沒有找到,因為我的數據必須要求命中率時100%,我只能通過調整slab的增長因子和page的大小來盡量來使命中率 接近100%,但是並不能100%保證命中率是100%(這話怎么讀起來這么別扭呢,自我檢討一下自己的語文水平),如果您說,這種方案不行啊,因為我的 memcached server不能停啊,不要緊還有另外一個方法,就是memcached-tool,執行move命令,如:move 3 1,代表把3號slab中的一個內存頁移動到1號slab中,有人問了,這有什么用呢,比如說我的20號slab的利用率非常低,但是page卻又很多, 比如200,那么就是200m,而2好slab經常發生LRU,明顯page不夠,我就可以move 20 2,把20號slab的一個內存頁移動到2號slab上,這樣就能更加有效的利用內存了(有人說了,一次只移動一個page,多麻煩啊?ahuaxuan 說,還是寫個腳本,循環一下吧)。

有人說不行啊,我的memcache中的數據不能丟失啊,ok,試試新浪的memcachedb吧,雖然我沒有用過,但是建議大家可以試試,它也使利用 memcache協議和berkeleyDB做的(寫到這里,我不得不佩服danga了,我覺得它最大的貢獻不是memcache server本身,而是memcache協議),據說它被用在新浪的不少應用上,包括新浪的博客。

補充,stats slab命令可以查看memcached中slab的情況,而stats命令可以查看你的memcached的一些健康情況,比如說命中率之類的,示例如下: 

STAT pid 2232
STAT uptime 1348
STAT time 1218120955
STAT version 1.2.1
STAT pointer_size 32
STAT curr_items 0
STAT total_items 0
STAT bytes 0
STAT curr_connections 1
STAT total_connections 3
STAT connection_structures 2
STAT cmd_get 0
STAT cmd_set 0
STAT get_hits 0
STAT get_misses 0
STAT bytes_read 26
STAT bytes_written 16655
STAT limit_maxbytes 104857600

從上面的數據可以看到這個memcached進程的命中率很好,get_misses低達0個,怎么回事啊,因為這個進程使我剛啟動的,我只用 telnet連了一下,所以curr_connections為1,而total_items為0,因為我沒有放數據進去,get_hits為0,因為我 沒有調用get方法,最后的結果就是misses當然為0,哇哦,換句話說命中率就是100%,又yy了。

該到總結的時候了,從這篇文章里我們可以得到以下幾個結論:
結論一,memcached得LRU不是全局的,而是針對slab的,可以說是區域性的。
結論二,要提高memcached的命中率,預估我們的value大小並且適當的調整內存頁大小和增長因子是必須的。
結論三,帶着問題找答案理解的要比隨便看看的效果好得多。 

參考:http://ahuaxuan.iteye.com/blog/225692


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM