1、memcache基本簡介
memcached是高性能的分布式內存緩存服務器。一般的使用目的是,通過緩存數據庫查詢結果,減少數據庫訪問次數,以提高動態Web應用的速度、提高可擴展性。
Memcache的運行圖:

Memcache的特征
memcached作為高速運行的分布式緩存服務器,具有以下的特點。
1、基於C/S架構協議簡單
memcached的服務器客戶端通信並不使用復雜的XML等格式,而使用簡單的基於文本行的協議。 因此,通過telnet也能在memcached上保存數據、取得數據。
2、基於libevent的事件處理
libevent是個程序庫,它將Linux的epoll、BSD類操作系統的kqueue等事件處理功能封裝成統一的接口。即使對服務器的連接數增加,也能發揮O(1)的性能。memcached使用這個libevent庫,因此 能在Linux、BSD、Solaris等操作系統上發揮其高性能。
3、內置內存存儲方式
為了提高性能,memcached中保存的數據都存儲在memcached內置的內存存儲空間中。由於數據僅存在於內存中,因此重啟memcached、重啟操作系統會導致全部數據消失。另外,內容容量達到指值之后,就基於LRU(Least Recently Used)算法自動刪除不使用的緩存。memcached本身是為緩存 而設計的服務器,因此並沒有過多考慮數據的永久性問題。
4、memcached不互相通信的分布式
memcached盡管是“分布式”緩存服務器,但服務器端並沒有分布式功能。各個memcached不會互 相通信以共享信息。那么,怎樣進行分布式呢?這完全取決於客戶端的實現。(如下圖所示)

2、理解memcache的內存存儲
2.1、存儲機制
Memcache采用的是Slab Allocator方式進行存儲數據。這一機制可以很好的整理內存,以便重復利用,從而解決了內存碎片的問題。在該機制出現以前,內存的分配是通過對所有記錄簡單地進行malloc和free來進行的。但是,這種方式會導致內存碎片,加重操作系統內存管理器的負擔,最壞的情況下,會導致操作系統比memcached進程本身還慢。
2.2、Slab Allocator基本原理
1、按照預先規定的大小,將分配的內存以page(默認每個page為1M)為單位分為特定的塊(chunk),並且把相同大小的chunk分成組(chunk的集合);
2、存儲數據時,將會尋找與value大小相近的chunk區域進行存儲;
3、內存一旦以page的形式分配出去,在重啟前不會被回收或者重新分配,以解決內存碎片問題。(分配的內存不會釋放,而是重復利用)
2.3、理解四個名詞
【可參考下面的形象解析圖進行理解】
Slab
用於表示存儲的最大size數據,僅僅只是用於定義(通俗的講就是表示可以存儲數據大小的范圍)。默認情況下,前后兩個slab表示存儲的size以1.25倍進行增長。例如slab1為96字節,slab2為120字節
Page
分配給Slab的內存空間,默認為1MB。分給Slab后將會根據slab的大小切割成chunk
Chunk
用於緩存記錄的內存空間
Slab calss
特定大小的Chunk集合
2.4、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。
形象解析圖:(這圖湊合湊合就好了哈,不是很專業2333)

2.5、Memcache存儲具體過程
Memcached並不是將所有大小的數據都放在一起的,而是預先將數據空間划分為一系列slabs,每個slab只負責一定范圍內的數據存儲。memcached根據收到的數據的大小,選擇最適合數據大小的slab。假若這個slab仍有空閑chunk的列表,根據該列表選擇chunk,然后將數據緩存於其中;若無則申請page(1M)【可以參考上面我畫的形象圖23333】
具體分析:從上面我們了解到slab的作用。Slab的增長因子默認以1.25倍進行增長。那為什么會導致有些不是1.25倍呢?答案是受小數的影響,你可以使用-f int測試個整數增長因子看看效果。【后面具體講解】
以下圖進行分析,例如slab中112字節,表示可以存儲大於88字節且小於或等於112字節的value。

2.6、Slab Allocator缺點
Slab Allocator解決了當初的內存碎片問題,但新的機制也給memcached帶來了新的問題。
這個問題就是,由於分配的是特定長度的內存,因此無法有效利用分配的內存。例如,將100字節 的數據緩存到128字節的chunk中,剩余的28字節就浪費了(如下圖所示)。

2.7、使用-f增長因子進行調優
增長因子就是相鄰兩個chunk之間的增長倍數。這個參數memcache默認是1.25,但是我們先采用整數2來測試一下,看看效果。

由圖中我們可以看到chunk size的增長是2倍的。
我們再來看看-f 1.25的效果

為什么1.25倍增長因子就不能保證全部相鄰的chunk size是1.25倍增長呢?
因為這些誤差是為了保持字節數的對齊而故意設置的。
兩圖一對比,可見,因子為1.25組間差距比因子為2時小得多,更適合緩存幾百字節的記錄。
因此,使用memcached時,最好是重新計算一下數據的預期平均長度,調整growth factor,以獲得最恰當的設置。
3、memcache刪除機制
從上面我們知道,已經分配出去的內存是不會被釋放回收的,記錄超時后,客戶端就無法看到該記錄,其存儲空間即可重復使用。
3.1、Lazy Expiration
memcached內部不會監視記錄是否過期,而是在get時查看記錄的時間戳,檢查記錄是否過期。這種技術被稱為lazy(惰性)expiration。因此,memcached不會在過期監視上耗費CPU時間。
3.2、LRU刪除
memcached會優先使用已超時的記錄的空間,但即使如此,也會發生追加新記錄時空間不足的情況, 此時就要使用名為Least Recently Used(LRU)機制來分配空間。顧名思義,這是刪除“最近最少 使用”的記錄的機制。因此,當memcached的內存空間不足時(無法從slab class獲取到新的空間時),就從最近未被使用的記錄中搜索,並將其空間分配給新的記錄。從緩存的實用角度來看,該模型十分理想。
不過,有些情況下LRU機制反倒會造成麻煩。memcached啟動時通過“M”參數可以禁止LRU。

啟動時必須注意的是,小寫的“m”選項是用來指定最大內存大小的。不指定具體數值則使用默認 值64MB。
指定“M”參數啟動后,內存用盡時memcached會返回錯誤。話說回來,memcached畢竟不是存儲器,而是緩存,所以推薦使用LRU。
4、啟動memcache參數
【黑體字的參數較為常用】
| -p<num> |
監聽的TCP端口(默認:11211) |
| -U<num> |
UDP監聽端口(默認:11211 0關閉) |
| -d |
以守護進程方式運行 |
| -u<username> |
指定用戶運行 |
| -m<num>. |
最大內存使用,單位MB。默認64MB |
| -c<num> |
最大同時連接數,默認是1024 |
| -v |
輸出警告和錯誤消息 |
| -vv |
打印客戶端的請求和返回信息 |
| -h |
幫助信息 |
| -l<ip> |
綁定地址(默認任何ip地址都可以訪問) |
| -P<file> |
將PID保存在file文件 |
| -i |
打印memcached和libevent版權信息 |
| -M |
禁止LRU策略,內存耗盡時返回錯誤 |
| -f<factor> |
增長因子,默認1.25 |
| -n<bytes> |
初始chunk=key+suffix+value+32結構體,默認48字節 |
| -L |
啟用大內存頁,可以降低內存浪費,改進性能 |
| -l |
調整分配slab頁的大小,默認1M,最小1k到128M |
| -t<num> |
線程數,默認4。由於memcached采用NIO,所以更多線程沒有太多作用 |
| -R |
每個event連接最大並發數,默認20 |
| -C |
禁用CAS命令(可以禁止版本計數,減少開銷) |
| -b |
Set the backlog queue limit (default: 1024) |
| -B |
Binding protocol-one of ascii, binary or auto (default) |
| -s<file> |
UNIX socket |
| -a<mask> |
access mask for UNIX socket, in octal (default: 0700) |
5、Memcache指令匯總
| 指令 |
描述 |
例子 |
| get key |
#返回對應的value |
get mykey |
| set key 標識符 有效時間 長度 |
key不存在添加,存在更新 |
set mykey 0 60 5 |
| add key標識符 有效時間 長度 |
#添加key-value值,返回stored/not_stored |
add mykey 0 60 5 |
| replace key標識符 有效時間 長度 |
#替換key中的value,key存在成功返回stored,key不存在失敗返回not_stored |
replace mykey 0 60 5 |
| append key標識符 有效時間 長度 |
#追加key中的value值,成功返回stored,失敗返回not_stored |
append mykey 0 60 5 |
| prepend key標識符 有效時間 長度 |
#前置追加key中的value值,成功返回stored,失敗返回not_stored
|
prepend mykey 0 60 5 |
| incr key num |
#給key中的value增加num。若key中不是數字,則將使用num替換value值。返回增加后的value |
Incre mykey 1 |
| decr |
#同上 |
同上 |
| delete key [key2…] |
刪除一個或者多個key-value。成功刪除返回deleted,不存在失敗則返回not_found |
delete mykey |
| flush_all [timeount] |
#清除所有[timeout時間內的]鍵值,但不會刪除items,所以memcache依舊占用內存 |
flush_all 20 |
| version |
#返回版本號 |
version |
| verbosity |
#日志級別 |
verbosity |
| quit |
#關閉連接 |
quit |
| stats |
#返回Memcache通用統計信息 |
stats |
| stats slabs |
#返回Memcache運行期間創建的每個slab的信息 |
stats slabs |
| stats items |
#返回各個slab中item的個數,和最老的item秒數 |
stats items |
| stats malloc |
#顯示內存分配數據 |
stats malloc |
| stats detail [on|off|dump] |
#on:打開詳細操作記錄、off:關閉詳細操作記錄、dump顯示詳細操作記錄(每一個鍵的get、set、hit、del的次數) |
stats detail on stats detail off stats detail dump |
| stats cachedump slab_id limit_num |
#顯示slab_id中前limit_num個key |
stats cachedump 1 2 |
| stats reset |
#清空統計數據 |
stats reset |
| stats settings |
#查看配置設置 |
stats settings |
| stats sizes |
#展示了固定chunk大小中的items的數量 |
Stats sizes |
注意:標識符:一個十六進制無符號的整數(以十進制來表示),需和數據一起存儲,get的時候一起返回
ps:最近老是思考以后的方向,感覺有點迷茫,都不能好好學習了。要盡快調整好心態,切勿浮躁,欲速則不達。
參考資料:
1、Memcached原理與使用詳解 作者:heiyeluren(黑夜路人)
http://blog.csdn.net/heiyeshuwu
2 、memcached 全面剖析 作者:長野雅廣、前坂徹 charlee 譯
3、Memcache 內存分配策略和性能(使用)狀態檢查 作者:jyzhou
http://www.cnblogs.com/zhoujinyi/p/5554083.html
作者:那一葉隨風
聲明:轉載時請在文章頁面明顯位置給出原文鏈接
