Free & open source, high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database load.
memcached是一個免費開源、高性能、分佈式的內存對象緩存系統,本質上是通用的,但旨在經過加速動態Web應用程序來減輕數據庫負載。php
Memcache是一款開發工具,其設計思想主要反映如下幾個方面:html
1.客戶端二者區別:web
2.服務器端二者區別:算法
ps:本文說明的內容是關於memcache服務器的,請不要跟客戶端的叫法混淆。數據庫
1. Memcached 的高性能緩存
首先從內存模型來研究memcached:C++裏分配內存有兩種方式,預先分配和動態分配內存,顯然預先分配內存會使程序比較快,可是它的缺點是不能有效利用內存;而動態分配能夠有效利用內存,可是會使程序運行效率降低,memcached的內存分配就是基於以上原理,顯然爲了得到更快的速度,有時候咱們不得不以空間換時間。
Memcached的高性能源於兩階段哈希(two-stage-hash)結構。Memcached就像一個巨大的、存儲了不少<key, value>對的哈希表,經過key能夠存儲或查詢任意的數據。客戶端能夠把數據存儲在多臺memcached上,當查詢數據時,客戶端首先參考節點列表計算出key的哈希值(階段一哈希),進而選中一個節點;客戶端將請求發送給選中的節點,而後memcached節點經過一個內部的哈希算法(階段二哈希),查找真正的數據(item)並返回個客戶端。從實現的角度看,memcached是一個非阻塞、基於事件驅動的服務器程序。
爲了提升性能,memcacahed中保存的數據都存儲在memcached內置的內存存儲空間中。因爲數據僅存在於內存中,所以重啓memcached、重啓操做系統會致使所有數據丟失。另外,內存容量達到指定值以後,就基於LRU(Least Recently Used)算法自動刪除不使用的緩存。memcached自己是爲了緩存而設計的服務器,所以並無過多考慮數據的永久性問題。服務器
2. Slab Allocator 內存分配、管理機制app
1.在該機制出現之前,內存的分配是經過對全部記錄簡單地進行malloc和free 來進行的。可是,這種方式會致使內存碎片,加劇操做系統內存管理器的負擔,最壞的狀況下,會致使操做系統比memcached 進程自己還慢。Slab Allocator 就是爲解決該問題而誕生的, 它按照預先規定的大小,將分配的內存分割成特定長度的塊,以完成解決內存碎片的問題。存儲結構圖以下:框架
Memcached的存儲涉及到slab、page、chunk三個概念
chunk:固定大小的內存空間,用於緩存記錄,默認爲88Byte。
page:分配給Slab的內存空間,默認是1M。分配給Slab以後根據Slab的大小切成chunk。
slab:一樣大小的chunk組成一類slab。分佈式
2.在Slab中緩存記錄的原理
memcached 根據收到的數據大小,選擇最適合數據大小的Slab,memcached中保存着Slab內空閒chunk的列表,根據該列表選擇chunk,而後將數據緩存其中。Slab allocator分配的內存不會釋放,而是重複利用。
3.Slab Allocator 的缺點
因爲分配的是特定長度的內存,所以沒法有效的利用分配的內存。對與該問題沒有完美的解決方案,但能夠調節slab class的大小差異來減小空間浪費。
4.使用Growth Factor進行調優
memcached在啓動時制定Growth Factor因子(經過f選項),就能夠在某種程度上控制slab之間的差別。默認值時1.25.
memcached是緩存,不須要永久的保存到服務器上,下面介紹它的刪除機制:
1.Lazy Expiration
memcached 不會釋放已經分配的內存,記錄過時後,客戶端沒法在看到這條記錄,其存儲空間能夠再利用。memcached內部不會監視記錄是否過時,而是在get時查看記錄的時間戳,檢查記錄是否過時。這種技術被稱爲Lazy Expiration。所以,memcached不會在過時監視上耗費CPU時間。
2.LRU(Least Recently Used)
memcached會優先使用已超時的記錄空間,但即便如此,也會發生追加新記錄時空間不足的狀況,此時就要使用名爲LRU機制來分配空間。顧名思義,這是刪除「最近最少使用」記錄的機制。所以,當memcahced的內存空間不足時(沒法從slab class獲取新的空間時),就從最近未被使用的記錄中搜索,並將其空間分配給新的記錄。
1.Memcached "分佈式"
特別澄清一個問題:MemCache雖然被稱爲"分佈式緩存",可是MemCache自己徹底不具有分佈式的功能,Memcache集羣之間不會相互通訊(與之造成對比的,好比JBoss Cache,某臺服務器有緩存數據更新時,會通知集羣中其餘機器更新緩存或清除緩存數據),所謂的"分佈式",徹底依賴於客戶端程序的實現。前面已經講過memcached的兩段哈希了,數據的保存和獲取都使用相同的算法。這樣將不一樣的鍵保存到不一樣的服務器上,就實現了memcached的分佈式。Memcached服務器增多後,鍵就會分散,即便一臺memcached服務器發生故障沒法鏈接,也不會影響其餘的緩存,系統依然能繼續進行。
2.餘數分佈式算法
就是「根據服務器臺數的餘數進行分散」。求得鍵的整數哈希值,再除以服務器臺數,根據其他數來選擇服務器。
餘數算法的缺點:餘數計算的方法簡單,數據的分散性也至關優秀,但也有其缺點。那就是當添加或移除服務器時,緩存重組的代價至關巨大。添加服務器後,餘數就會產生鉅變,這樣就沒法獲取與保存時相同的服務器,從而影響緩存的命中率。
3.Consistent hashing(一致哈希)
首先求出memcached 服務器(節點)的哈希值,並將其配置到0~2^32-1 的圓(continuum)上。而後用一樣的方法求出存儲數據的鍵的哈希值,並映射到圓上。而後從數據映射到的位置開始順時針查找,將數據保存到找到的第一個服務器上。若是超過2^32-1仍然找不到服務器,就會保存到第一臺memcached 服務器上。
從上圖的狀態中添加一臺Memcached 服務器。餘數分佈式算法因爲保存鍵的服務器會發生巨大變化,而影響緩存的命中率,但Consistent Hashing中,只有在continuum 上增長服務器的地點逆時針方向的第一臺服務器上的鍵會受到影響。
Consistent Hashing(添加服務器):
所以,Consistent Hashing 最大限度地抑制了鍵的從新分佈。並且,有的Consistent Hashing 的實現方法還採用了虛擬節點的思想。使用通常的hash函數的話,服務器的映射地點的分佈很是不均勻。所以,使用虛擬節點的思想,爲每一個物理節點(服務器)在continum上分配100~200 個點。這樣就能抑制分佈不均勻,最大限度地減少服務器增減時的緩存從新分佈。
經過上文中介紹的使用Consistent Hashing 算法的Memcached 客戶端函數庫進行測試的結果是,由服務器臺數(n)和增長的服務器臺數(m)計算增長服務器後的命中率計算公式: (1 -m/(n+m)) * 100
1.存儲及刪除命令,簡單易用,使用語法以下:
command <key> <flags> <expiration time> <bytes>
<value>
參數說明以下:
command set/add/replace
key key 用於查找緩存值
flags 能夠包括鍵值對的整型參數,客戶機使用它存儲關於鍵值對的額外信息
expiration time 在緩存中保存鍵值對的時間長度(以秒爲單位,0 表示永遠)
bytes 在緩存中存儲的字節數
value 存儲的值(始終位於第二行)
2.讀取命令
3.統計命令
1.stats指令解讀
stats是一個比較重要的指令,用於列出當前MemCache服務器的狀態,返回的參數反映着Memcache服務器的基本信息,他們的意思是:
參 數 名 | 做 用 |
---|---|
pid | MemCache服務器的進程id |
uptime | 服務器已經運行的秒數 |
time | 服務器當前的UNIX時間戳 |
version | MemCache版本 |
pointer_size | 當前操做系統指針大小,反映了操做系統的位數,64意味着MemCache服務器是64位的 |
rusage_user | 進程的累計用戶時間 |
rusage_system | 進程的累計系統時間 |
curr_connections | 當前打開着的鏈接數 |
total_connections | 當服務器啓動之後曾經打開過的鏈接數 |
connection_structures | 服務器分配的鏈接構造數 |
cmd_get | get命令總請求次數 |
cmd_set | set命令總請求次數 |
cmd_flush | flush_all命令總請求次數 |
get_hits | 總命中次數,重要,緩存最重要的參數就是緩存命中率,以get_hits / (get_hits + get_misses)表示,好比這個緩存命中率就是99.2% |
get_misses | 總未命中次數 |
auth_cmds | 認證命令的處理次數 |
auth_errors | 認證失敗的處理次數 |
bytes_read | 總讀取的字節數 |
bytes_written | 總髮送的字節數 |
limit_maxbytes | 分配給MemCache的內存大小(單位爲字節) |
accepting_conns | 是否已經達到鏈接的最大值,1表示達到,0表示未達到 |
listen_disabled_num | 統計當前服務器鏈接數曾經達到最大鏈接的次數,這個次數應該爲0或者接近於0,若是這個數字不斷增加, 就要當心咱們的服務了 |
threads | 當前MemCache總線程數,因爲MemCache的線程是基於事件驅動機制的,所以不會一個線程對應一個用戶請求 |
bytes | 當前服務器存儲的items總字節數 |
current_items | 當前服務器存儲的items總數量 |
total_items | 自服務器啓動之後存儲的items總數量 |
比較關注的點:get_hits 和 get_misses,命中率:get_hits/(get_hits + get_misses) 是衡量memcache服務器的一個重要指標。
2.stats slabs 指令解讀
參 數 名 | 做 用 |
---|---|
chunk_size | 當前slab每一個chunk的大小,單位爲字節 |
chunks_per_page | 每一個page能夠存放的chunk數目,因爲每一個page固定爲1M即10241024字節,因此這個值就是(10241024/chunk_size) |
total_pages | 分配給當前slab的page總數 |
total_chunks | 當前slab最多可以存放的chunk數,這個值是total_pages*chunks_per_page |
used_chunks | 已經被分配給存儲對象的chunks數目 |
free_chunks | 曾經被使用過可是由於過時而被回收的chunk數 |
free_chunks_end | 新分配但尚未被使用的chunk數,這個值不爲0則說明當前slab歷來沒有出現過容量不夠的時候 |
mem_requested | 當前slab中被請求用來存儲數據的內存空間字節總數,(total_chunks*chunk_size)-mem_requested表示有多少內存在當前slab中是被閒置的,這包括未用的slab+使用的slab中浪費的內存 |
get_hits | 當前slab中命中的get請求數 |
cmd_set | 當前slab中接收的全部set命令請求數 |
delete_hits | 當前slab中命中的delete請求數 |
incr_hits | 當前slab中命中的incr請求數 |
decr_hits | 當前slab中命中的decr請求數 |
cas_hits | 當前slab中命中的cas請求數 |
cas_badval | 當前slab中命中可是更新失敗的cas請求數 |
經過此命令返回的信息,能夠查看slab class的分佈狀況,以及每一個slab中chunk使用狀況;根據slab的分佈,能夠判斷增加因子的設置的是否合理等。
3.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已經負載太重了。
https://www.cnblogs.com/xrq73...
http://zhihuzeye.com/archives...