Memcached性能檢測
——————————————————分割線————————————————————
Memcached作爲一個內存key-value存儲容器有非常優秀的性能,但是在上次的使用中確發現大量的數據丟失情況發生,導致cache的功能基本消失。具體的檢測方式如下:檢測命中率
檢測命中率是一個最基本的、最宏觀的方式,使用telnet連接到memcached服務器,然後執行stats命令就可以看到宏觀的一些信息,如下圖。
這個命令中比較關鍵的屬性是get_hits和get_misses,get_hits表示讀取cache命中的次數,get_misses是讀取失敗的次數,即嘗試讀取不存在的緩存數據。
命中率=get_hits / (get_hits + get_misses)
命中率越高說明cache起到的緩存作用越大。但是在實際使用中,這個命中率不是有效數據的命中率,有些時候get操作可能只是檢查一個key存在不存在,這個時候miss也是正確的,這就像用memcached作爲一種定時器,將一些臨時數據在memcache中存放特定時間長度,業務邏輯會根據cache是否存在而作不同的邏輯,這種數據其實已經不是單純的緩存了,也不應該統計到命中率中。再者,這個命中率是從memcached啓動開始所有的請求的綜合值,不能反映一個時間段內的情況,所以要排查memcached的性能問題,還需要更詳細的數值。但是高的命中率還是能夠反映出memcached良好的使用情況,突然下跌的命中率能夠反映大量cache丟失的發生。
stats :轉儲所連接的memcached 實例的當前統計數據。
flush_all:用於清理緩存中的所有名稱/值對。如果您需要將緩存重置到乾淨的狀態,則flush_all 能提供很大的用處。
查看memcached狀態的基本命令,通過這個命令可以看到如下信息:
STAT pid 22459 進程ID
STAT uptime 1027046 服務器運行秒數
STAT time 1273043062 服務器當前unix時間戳
STAT version 1.4.4 服務器版本
STAT pointer_size 64 操作系統字大小(這臺服務器是64位的)
STAT rusage_user 0.040000 進程累計用戶時間
STAT rusage_system 0.260000 進程累計系統時間
STAT curr_connections 10 當前打開連接數
STAT total_connections 82 曾打開的連接總數
STAT connection_structures 13 服務器分配的連接結構數
STAT cmd_get 54 執行get命令總數
STAT cmd_set 34 執行set命令總數
STAT cmd_flush 3 指向flush_all命令總數
STAT get_hits 9 get命中次數
STAT get_misses 45 get未命中次數
STAT delete_misses 5 delete未命中次數
STAT delete_hits 1 delete命中次數
STAT incr_misses 0 incr未命中次數
STAT incr_hits 0 incr命中次數
STAT decr_misses 0 decr未命中次數
STAT decr_hits 0 decr命中次數
STAT cas_misses 0 cas未命中次數
STAT cas_hits 0 cas命中次數
STAT cas_badval 0 使用擦拭次數
STAT auth_cmds 0
STAT auth_errors 0
STAT bytes_read 15785 讀取字節總數
STAT bytes_written 15222 寫入字節總數
STAT limit_maxbytes 1048576 分配的內存數(字節)
STAT accepting_conns 1 目前接受的鏈接數
STAT listen_disabled_num 0
STAT threads 4 線程數
STAT conn_yields 0
STAT bytes 0 存儲item字節數
STAT curr_items 0 item個數
STAT total_items 34 item總數
STAT evictions 0 爲獲取空間刪除item的總數
Stats items
Stats items命令可以查看每個slab中存儲的item的一些詳細信息,具體可以見下圖。
關鍵屬性有:
stats items可以詳細的觀察各slab的數據對象的情況,因爲memcached的內存分配策略導致一旦memcached的總內存達到了設置的最大內存,代表所有的slab能夠使用的page都已經固定,這個時候如果還有數據放入,將開始導致memcached使用LRU策略剔除數據。而LRU策略不是針對所有的slabs,而是隻針對新數據應該被放入的slab,例如有一個新的數據要被放入slab 3,則LRU只對slab 3進行。通過stats items就可以觀察到這些剔除的情況。
具體分析如下:
evicted屬性
如果一個slab的evicted屬性不是0,則說明當前slab出現了提前剔除數據的情況,這個slab可能是你需要注意的。evicted_time屬性
如果evicted不爲0,則evicited_time就代表最後被剔除的數據時間緩存的時間。並不是發生了LRU就代碼memcached負載過載了,因爲有些時候在使用cache時會設置過期時間爲0,這樣緩存將被存放30天,如果內存慢了還持續放入數據,而這些爲過期的數據很久沒有被使用,則可能被剔除。需要注意的是,最後剔除的這個數據已經被緩存的時間,把evicted_time換算成標準時間看下是否已經達到了你可以接受的時間,例如:你認爲數據被緩存了2天是你可以接受的,而最後被剔除的數據已經存放了3天以上,則可以認爲這個slab的壓力其實可以接受的;但是如果最後被剔除的數據只被緩存了20秒,不用考慮,這個slab已經負載過重了。age屬性
age屬性反應了當前還在緩存的數據中最久的時間,它的大小和evicted_time沒有必然的大小關係,因爲可能時間最久的數據確實頻繁被讀取的,這時候不會被LRU清理掉,但是如果它小於evicted_time的話,則說明數據在被下去讀取前就被清理了,或者存放了很多長時間但是不被使用的緩存對象。Stats slabs
從Stats items中如果發現有異常的slab,則可以通過stats slabs查看下該slab是不是內存分配的確有問題。
Stats slabs結果如下圖
Stats slabs的屬性說明如下:
chunk_size | 當前slab每個chunk的大小 |
chunk_per_page | 每個page能夠存放的chunk數 |
total_pages | 分配給當前slab的page總數 |
total_chunks | 當前slab最多能夠存放的chunk數,應該等於chunck_per_page * total_page |
used_chunks | 已經被佔用的chunks總數 |
free_chunks | 過期數據空出的chunk裏還沒有被使用的chunk數 |
free_chunks_end | 新分配的但是還沒有被使用的chunk數 |
這個命令的信息量很大,所有屬性都很有價值。下面一一解釋各屬性:
綜合上面的數據,可以發現造成memcached的內存使用率降低的屬性有:
chunk_size, chunk_per_page
這兩個屬性是固定的,但是它反映當前slab存儲的數據大小,可以供你分析緩存數據的散列區間,通過調整增長因子可以改變slab的區間分佈,從而改變數據散列到的區域。如果大量的230byte到260byte的數據,而剛好一個slab大小是250byte,則250byte到260byte的數據將被落到下一個slab,從而導致大量的空間浪費。total_pages
這個是當前slab總共分配大的page總數,如果沒有修改page的默認大小的情況下,這個數值就是當前slab能夠緩存的數據的總大小(單位爲M)。如果這個slab的剔除非常嚴重,一定要注意這個slab的page數是不是太少了。
我上次處理的那個項目因爲和另外的一個項目共用的memcache,而且memcache已經運行了很長時間,導致page都已經全部被分配完,而剛好兩個項目的緩存數據大小差別很多,導致新項目數據最多的slab 4竟然只有一個page,所以數據緩存不到22s就被替換了,完全失去了緩存的意義。
針對我遇到的那個情況,解決方案是重新分配page,或者重啓memcache服務。但是page reassign方法從1.2.8版已經完全移除了,所以現在沒有辦法在線情況下重新分配page了。另外一種有些時候是不可以接受的,因爲一次緩存服務器的重啓將導致所有緩存的數據將重新從DB取出,這個可能造成db的壓力瞬間增大。而且有的緩存數據時不入庫的,這個時候我們就需要做memcache的導入和導出了。在下篇文章中我會總結下memcache的dump操作。total_chunks
這個的作用和total_pages基本相同,不過這個屬性可以更準確的反應實際可以存放的緩存對象總數。used_chunks, free_chunks, free_chunks_end
這三個屬性相關度比較高,從數值上來看它們滿足:
total_chunks = used_chunks + free_chunks + free_chunks_end
used_chunks就是字面的意思,已經使用的chunk數;free_chunks卻不是所有的未被使用的chunk數,而是曾經被使用過但是因爲過期而被回收的chunk數;free_chunks_end是page中從來沒有被使用過的chunk數。
從上圖可以看出,slab 1只放了一個對象,但是已經申請了一整個page,這個時候used_chunks爲1,但是free_chunks卻爲0,因爲還沒有任何回收的空間,而free_chunks_end卻等於10081,說明這麼多的chunk從來沒有被使用過。下圖就是這個數據過期後的stats slabs數據,可以發現free_chunks有值了,就是過期的那個chunk,所以是1,used_chunks爲0,free_chunks_end不變。
爲什麼要分兩種free chunk呢?
我的理解是這樣的:如果free_chunks_end不爲零,說明當前slab沒有出現過容量不夠的時候;而如果free_chunks始終爲0,說明很多數據過期時間過長或者在過期前就被剔除了,這個要結合剔除數據和數據保留的時間(age屬性)來看待。所以分開統計這兩個值可以準確的判斷實際空閒的chunk的狀態,一旦所以的chunk被使用過一次以後,除非重新申請page,否則free_chunks_end始終爲0。所以對於運行時間比較久的memcached,可能大部分這個值都是0。active_slabs,
total_malloced
在stats slabs輸出的最後兩項是兩個統計數據,一個是活動的slab總數,因爲slab雖然帶編號,但是這個編號不一定是連續的,因爲有可能有些中間區間的slab沒有值就沒有初始化,這樣以後該slab有值的時候就不用改變slab的編號了。所以活動的slab總數不一定等於slab的最大編號。
total_malloced這個是實際已經分配的總內存數,單位爲byte,這個數值決定了memcached實際還能申請多少內存,如果這個值已經達到設定的上限,則不會有新的page被分配,以前分配的page也已經固定slab了。
綜合上面的數據,可以發現造成memcached的內存使用率降低的屬性有:
page中從來沒有被使用過的chunks;chunk中存放數據和chunk實際大小的差值;由於短時間的數據集中在某個slab區域,導致大量page被分配,而之後被閒置的內存,這些即使有整個page的空閒也不會被分配給實際壓力很大的slab區域(這個功能是不是以後memcached會考慮實現呢?)。