前言:
一直在使用Memcache,但是對其內部的問題,如它內存是怎麼樣被使用的,使用一段時間後想看看一些狀態怎麼樣?一直都不清楚,查了又忘記,現在整理出該篇文章,方便自己查閱。
1:參數
上面加粗的參數,需要重點關注,正常啓動的例子:
可以通過命令查看所有參數:stats settings
2:理解memcached的內存存儲機制
Memcached默認情況下采用了名爲Slab Allocator的機制分配、管理內存。在該機制出現以前,內存的分配是通過對所有記錄簡單地進行malloc和free來進行的。但是,這種方式會導致內存碎片,加重操作系統內存管理器的負擔,最壞的情況下,會導致操作系統比memcached進程本身還慢。Slab Allocator就是爲解決該問題而誕生的。
Slab Allocator的基本原理是按照預先規定的大小,將分配的內存以page爲單位,默認情況下一個page是1M,可以通過-I參數在啓動時指定,分割成各種尺寸的塊(chunk), 並把尺寸相同的塊分成組(chunk的集合),如果需要申請內存時,memcached會劃分出一個新的page並分配給需要的slab區域。page一旦被分配在重啓前不會被回收或者重新分配,以解決內存碎片問題。
Page
分配給Slab的內存空間,默認是1MB。分配給Slab之後根據slab的大小切分成chunk。
Chunk
用於緩存記錄的內存空間。
Slab Class
特定大小的chunk的組。
Memcached並不是將所有大小的數據都放在一起的,而是預先將數據空間劃分爲一系列slabs,每個slab只負責一定範圍內的數據存儲。memcached根據收到的數據的大小,選擇最適合數據大小的slab。memcached中保存着slab內空閒chunk的列表,根據該列表選擇chunk,然後將數據緩存於其中。
如圖所示,每個slab只存儲大於其上一個slab的size並小於或者等於自己最大size的數據。例如:100字節大小的字符串會被存到slab2(88-112)中,每個slab負責的空間是不等的,memcached默認情況下下一個slab的最大值爲前一個的1.25倍,這個可以通過修改-f參數來修改增長比例。
Slab Allocator解決了當初的內存碎片問題,但新的機制也給memcached帶來了新的問題。chunk是memcached實際存放緩存數據的地方,這個大小就是管理它的slab的最大存放大小。每個slab中的chunk大小是一樣的,如上圖所示slab1的chunk大小是88字節,slab2是112字節。由於分配的是特定長度的內存,因此無法有效利用分配的內存。例如,將100字節的數據緩存到128字節的chunk中,剩餘的28字節就浪費了。這裏需要注意的是chunk中不僅僅存放緩存對象的value,而且保存了緩存對象的key,expire time, flag等詳細信息。所以當set 1字節的item,需要遠遠大於1字節的空間存放。
memcached在啓動時指定 Growth Factor因子(通過-f選項), 就可以在某種程度上控制slab之間的差異。默認值爲1.25。
slab的內存分配具體過程如下:
Memcached在啓動時通過-m參數指定最大使用內存,但是這個不會一啓動就佔用完,而是逐步分配給各slab的。如果一個新的數據要被存放,首先選擇一個合適的slab,然後查看該slab是否還有空閒的chunk,如果有則直接存放進去;如果沒有則要進行申請,slab申請內存時以page爲單位,無論大小爲多少,都會有1M大小的page被分配給該slab(該page不會被回收或者重新分配,永遠都屬於該slab)。申請到page後,slab會將這個page的內存按chunk的大小進行切分,這樣就變成了一個chunk的數組,再從這個chunk數組中選擇一個用於存儲數據。若沒有空閒的page的時候,則會對改slab進行LRU,而不是對整個memcache進行LRU。
以上大致講解了memcache的內存分配策略,下面來說明如何查看memcache的使用狀況。
3,memcache狀態和性能查看
① 命中率 :stats命令
按照下面的圖來解讀分析
get_hits表示讀取cache命中的次數,get_misses是讀取失敗的次數,即嘗試讀取不存在的緩存數據。即:
命中率=get_hits / (get_hits + get_misses)
命中率越高說明cache起到的緩存作用越大。但是在實際使用中,這個命中率不是有效數據的命中率,有些時候get操作可能只是檢查一個key存在不存在,這個時候miss也是正確的,這個命中率是從memcached啓動開始所有的請求的綜合值,不能反映一個時間段內的情況,所以要排查memcached的性能問題,還需要更詳細的數值。但是高的命中率還是能夠反映出memcached良好的使用情況,突然下跌的命中率能夠反映大量cache丟失的發生。
② 觀察各slab的items的情況:Stats items命令
主要參數說明:
outofmemory | slab class爲新item分配空間失敗的次數。這意味着你運行時帶上了-M或者移除操作失敗 |
number | 存放的數據總數 |
age | 存放的數據中存放時間最久的數據已經存在的時間,以秒爲單位 |
evicted | 不得不從LRU中移除未過期item的次數 |
evicted_time | 自最後一次清除過期item起所經歷的秒數,即最後被移除緩存的時間,0表示當前就有被移除,用這個來判斷數據被移除的最近時間 |
evicted_nonzero | 沒有設置過期時間(默認30天),但不得不從LRU中稱除該未過期的item的次數 |
因爲memcached的內存分配策略導致一旦memcached的總內存達到了設置的最大內存,表示所有的slab能夠使用的page都已經固定,這時如果還有數據放入,將導致memcached使用LRU策略剔除數據。而LRU策略不是針對所有的slabs,而是隻針對新數據應該被放入的slab,例如有一個新的數據要被放入slab 3,則LRU只對slab 3進行,通過stats items就可以觀察到這些剔除的情況。
注意evicted_time:並不是發生了LRU就代表memcached負載過載了,因爲有些時候在使用cache時會設置過期時間爲0,這樣緩存將被存放30天,如果內存滿了還持續放入數據,而這些爲過期的數據很久沒有被使用,則可能被剔除。把evicted_time換算成標準時間看下是否已經達到了你可以接受的時間,例如:你認爲數據被緩存了2天是你可以接受的,而最後被剔除的數據已經存放了3天以上,則可以認爲這個slab的壓力其實可以接受的;但是如果最後被剔除的數據只被緩存了20秒,不用考慮,這個slab已經負載過重了。
通過上面的說明可以看到當前的memcache的slab1的狀態:
items有305816個,有效時間最久的是21529秒,通過LRU移除未過期的items有95336839個,通過LRU移除沒有設置過期時間的未過期items有95312220個,當前就有被清除的items,啓動時沒有帶-M參數。
③ 觀察各slabs的情況:stats slabs命令
從Stats items中如果發現有異常的slab,則可以通過stats slabs查看下該slab是不是內存分配的確有問題。
主要參數說明:
屬性名稱 | 屬性說明 |
---|---|
chunk_size | 當前slab每個chunk的大小 |
chunk_per_page | 每個page能夠存放的chunk數 |
total_pages | 分配給當前slab的page總數,默認1個page大小1M,可以計算出該slab的大小 |
total_chunks | 當前slab最多能夠存放的chunk數,應該等於chunck_per_page * total_page |
used_chunks | 已經被佔用的chunks總數 |
free_chunks | 過期數據空出的chunk但還沒有被使用的chunk數 |
free_chunks_end | 新分配的但是還沒有被使用的chunk數 |
這裏需要注意:total_pages 這個是當前slab總共分配大的page總數,如果沒有修改page的默認大小的情況下,這個數值就是當前slab能夠緩存的數據的總大小(單位爲M)。如果這個slab的剔除非常嚴重,一定要注意這個slab的page數是不是太少了。還有一個公式:
total_chunks = used_chunks + free_chunks + free_chunks_end
另外stats slabs還有2個屬性:
屬性名稱 | 屬性說明 |
active_slabs |
活動的slab總數 |
total_malloced |
實際已經分配的總內存數,單位爲byte,這個數值決定了memcached實際還能申請多少內存,如果這個值已經達到設定的上限(和stats settings中的maxbytes對比),則不會有新的page被分配。 |
④ 對象數量的統計:stats sizes
注意:該命令會鎖定服務,暫停處理請求。該命令展示了固定chunk大小中的items的數量。也可以看出slab1(96byte)中有多少個chunks。
⑤ 查看、導出key:stats cachedump
在進入memcache中,大家都想查看cache裏的key,類似redis中的keys *命令,在memcache裏也可以查看,但是需要2步完成。
一是先列出items:
stats items --命令
...
...
STAT items:29:number 228
STAT items:29:age 34935
...
END
二是通過itemid取key,上面的id是29,再加上一個參數:爲列出的長度,0爲全部列出。
stats cachedump 29 0 --命令
ITEM 26457202 [49440 b; 1467262309 s]
...
ITEM 30017977 [45992 b; 1467425702 s]
ITEM 26634739 [48405 b; 1467437677 s]
END --總共228個key
get 26634739 取value
如何導出key呢?這裏就需要通過 echo ... nc 來完成了
echo "stats cachedump 29 0" | nc 10.211.55.9 11212 >/home/zhoujy/memcache.log
在導出的時候需要注意的是:cachedump命令每次返回的數據大小隻有2M,這個是memcached的代碼中寫死的一個數值,除非在編譯前修改。
⑥ 另一個監控工具:memcached-tool,一個perl寫的工具:memcache_tool.pl。
./memcached-tool 10.211.55.9:11212 --執行
# Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM
1 96B 20157s 28 305816 yes 95431913 0 0
2 120B 16049s 40 349520 yes 117041737 0 0
3 152B 17574s 39 269022 yes 92679465 0 0
4 192B 18157s 43 234823 yes 78892650 0 0
5 240B 18722s 52 227188 yes 72908841 0 0
6 304B 17971s 73 251777 yes 85556469 0 0
7 384B 17881s 81 221130 yes 75596858 0 0
8 480B 17760s 70 152880 yes 53553607 0 0
9 600B 18167s 58 101326 yes 34647962 0 0
10 752B 18518s 52 72488 yes 24813707 0 0
11 944B 18903s 52 57720 yes 16707430 0 0
12 1.2K 20475s 44 38940 yes 11592923 0 0
13 1.4K 21220s 36 25488 yes 8232326 0 0
14 1.8K 22710s 35 19740 yes 6232766 0 0
15 2.3K 22027s 33 14883 yes 4952017 0 0
16 2.8K 23139s 33 11913 yes 3822663 0 0
17 3.5K 23495s 31 8928 yes 2817520 0 0
18 4.4K 22611s 29 6670 yes 2168871 0 0
19 5.5K 23652s 29 5336 yes 1636656 0 0
20 6.9K 21245s 26 3822 yes 1334189 0 0
21 8.7K 22794s 22 2596 yes 783620 0 0
22 10.8K 22443s 19 1786 yes 514953 0 0
23 13.6K 21385s 18 1350 yes 368016 0 0
24 16.9K 23782s 16 960 yes 254782 0 0
25 21.2K 23897s 14 672 yes 183793 0 0
26 26.5K 27847s 13 494 yes 117535 0 0
27 33.1K 27497s 14 420 yes 83966 0 0
28 41.4K 28246s 14 336 yes 63703 0 0
29 51.7K 33636s 12 228 yes 24239 0 0
解釋:
列 | 含義 |
# | slab class編號 |
Item_Size | chunk大小 |
Max_age | LRU內最舊的記錄的生存時間 |
pages | 分配給Slab的頁數 |
count | Slab內的記錄數、chunks數、items數、keys數 |
Full? | Slab內是否含有空閒chunk |
Evicted | 從LRU中移除未過期item的次數 |
Evict_Time | 最後被移除緩存的時間,0表示當前就有被移除 |
OOM | -M參數? |
4,總結
實際應用Memcached時,我們遇到的很多問題都是因爲不瞭解其內存分配機制所致,希望本文能讓大家初步瞭解Memcached在內存方便的分配機制,雖然redis等一些nosql的數據庫產品在很多產品中替換了memcache,但是memcache還有很多項目會依賴它,所以還得學習來解決問題,後續出現新內容會不定時更新。
5,參考文檔