memcached 學習筆記 2

  • 原理

1 核心組件

Memcached有兩個核心組件組成:服務端(ms)和客戶端(mc)。php

首先mc拿到ms列表,並對key作hash轉化,根據hash值肯定kv對所存的ms位置。java

而後在一個memcached的查詢中,mc先經過計算key的hash值來肯定kv對所處在的ms位置。算法

當ms肯定後,客戶端就會發送一個查詢請求給對應的ms,讓它來查找確切的數據。數據庫

由於ms之間並無護衛備份,也就不須要互相通訊,因此效率較高。緩存

 

 

Memcached Client目前有一下四種:服務器

Memcached Client for Java,比 SpyMemcached更穩定、更早、更普遍;數據結構

SpyMemcached,比 Memcached Client for Java更高效;架構

XMemcached,比 SpyMemcache併發效果更好。 併發

alisoft-xplatform-asf-cache阿里軟件的架構師岑文初進行封裝的。裏面的註釋都是中文的,比較好memcached

 

2 memcached是怎麼工做的?

Memcached的神奇來自兩階段哈希(two-stage hash)。

Memcached就像一個巨大的、存儲了不少<key,value>對的哈希表。經過key,能夠存儲或查詢任意的數據。

客戶端能夠把數據存儲在多臺memcached上。

當查詢數據時,客戶端首先參考節點列表計算出key的哈希值(階段一哈希),進而選中一個節點;

客戶端將請求發送給選中的節點,而後memcached節點經過一個內部的哈希算法(階段二哈希),查找真正的數據(item)。

舉個列子,假設有3個客戶端1, 2, 3,3臺memcached A, B, C:
Client 1想把數據"barbaz"以key "foo"存儲。

Client 1首先參考節點列表(A, B, C),計算key "foo"的哈希值,假設memcached B被選中。

接着,Client 1直接connect到memcached B,經過key "foo"把數據"barbaz"存儲進去。  

Client 2使用與Client 1相同的客戶端庫(意味着階段一的哈希算法相同),也擁有一樣的memcached列表(A, B, C)。
因而,通過相同的哈希計算(階段一),Client 2計算出key "foo"在memcached B上,而後它直接請求memcached B,獲得數據"barbaz"。

各類客戶端在memcached中數據的存儲形式是不一樣的(perl Storable, php serialize, java hibernate, JSON等)。

一些客戶端實現的哈希算法也不同。可是,memcached服務器端的行爲老是一致的。

最後,從實現的角度看,memcached是一個非阻塞的、基於事件的服務器程序。這種架構能夠很好地解決C10K problem ,並具備極佳的可擴展性。

 

3 內存分配原理

Memcached有一個頗有特點的內存管理方式,爲了提升效率,它使用預申請和分組的方式管理內存空間,

而並非每次須要寫入數據的時候去malloc,刪除數據的時候free一個指針。

Memcached使用slab->chunk的組織方式管理內存。

 

3.1 Slab Allocation 機制(板 分配):

 

整理內存以便重複使用最近的 memcached 默認狀況下采用了名爲 Slab Allocator 的機制分配、 管理內存。

 

在該機制出現之前,內存的分配是經過對全部記錄簡單地進行 malloc 和 free 來進行的。 

 

可是,這種方式會致使內存碎片,加劇操做系統內存管理器的負擔,最壞的狀況下,會致使操做系統比 memcached 進程自己還慢。 

 

Slab Allocator 就是爲解決該問題而誕生的。

下面來看看 Slab Allocator 的原理。 

下面是 memcached 文檔中的 slab allocator 的目 標:

the primary goal of the slabs subsystem in memcached was to eliminate memory fragmentation issues

totally by using fixed­size memory chunks coming from a few predetermined size classes.

也就是說, Slab Allocator 的基本原理是按照預先規定的大小, 將分配的內存分割成特定長度的塊,以徹底解決內存碎片問題。

Slab Allocation 的原理至關簡單。 

將分配的內存分割成各類尺寸的塊( chunk), 並把尺寸相同的塊分紅組( chunk 的集合 slab class)(圖 2.1)。

 

 

memcached給Slab分配內存空間,默認是1MB。

分配給Slab以後 把slab的切分紅大小相同的chunk。

Chunk是用於緩存記錄的內存空間。

Slab Class特定大小的chunk的組。

並且, slab allocator 還有重複使用已分配的內存的目的。

也就是說,分配到的內存不會釋放,而是重複利用。

 

2 在 Slab 中緩存記錄的原理

memcached 根據收到的數據的大小, 選擇最適合數據大小的 slab(圖 )。

memcached 中保存着slab 內空閒 chunk 的列表,根據該列表選擇 chunk,而後將數據緩存於其中。

實際上, Slab Allocator 也是有利也有弊。 下面介紹一下它的缺點。

Slab Allocator 解決了當初的內存碎片問題, 但新的機制也給 memcached 帶來了新的問題。
這個問題就是, 因爲分配的是特定長度的內存,所以沒法有效利用分配的內存。

例如, 將 100 字節的數據緩存到 128 字節的 chunk 中, 剩餘的 28 字節就浪費了(圖 )。

 

對於該問題目前尚未完美的解決方案, 但在文檔中記載了比較有效的解決方案。

The most efficient way to reduce the waste is to use a list of size classes that closely matches (if that's at all

possible) common sizes of objects that the clients of this particular installation of memcached are likely tostore.

就是說,若是預先知道客戶端發送的數據的公用大小, 或者僅緩存大小相同的數據的狀況下, 只要使用適合數據大小的組的列表, 就能夠減小浪費。

可是很遺憾,如今還不能進行任何調優, 只能期待之後的版本了。 

可是, 咱們能夠調節 slab class的大小的差異。接下來講明 growth factor 選項。

 

3 使用 Growth Factor 進行調優

memcached 在啓動時指定 Growth Factor 因子(經過­f選項), 就能夠在某種程度上控制 slab 之間的差別。 默認值爲 1.25。

可是,在該選項出現以前,這個因子曾經固定爲 2, 稱爲「 powers of 2」策略。讓咱們用之前的設置,以 verbose 模式啓動 memcached 試試看:

$ memcached ­f 2 ­vv

下面是啓動後的 verbose 輸出:

slab class   1: chunk size    128 perslab  8192

slab class   2: chunk size    256 perslab  4096

slab class   3: chunk size    512 perslab  2048

slab class   4: chunk size   1024 perslab  1024

slab class   5: chunk size   2048 perslab   512

slab class   6: chunk size   4096 perslab   256

slab class   7: chunk size   8192 perslab   128

slab class   8: chunk size  16384 perslab    64

slab class   9: chunk size  32768 perslab    32

slab class  10: chunk size  65536 perslab    16

slab class  11: chunk size 131072 perslab     8

slab class  12: chunk size 262144 perslab     4

slab class  13: chunk size 524288 perslab     2

 

可見,從 128 字節的組開始, 組的大小依次增大爲原來的 2 倍。

這樣設置的問題是, slab 之間的差異比較大,有些狀況下就至關浪費內存。 所以,爲儘可能減小內存浪費,兩年前追加了 growth factor這個選項。

來看看如今的默認設置( f=1.25) 時的輸出(篇幅所限,這裏只寫到第 10 組):

slab class   1: chunk size     88 perslab 11915

slab class   2: chunk size    112 perslab  9362

slab class   3: chunk size    144 perslab  7281

slab class   4: chunk size    184 perslab  5698

slab class   5: chunk size    232 perslab  4519

slab class   6: chunk size    296 perslab  3542

slab class   7: chunk size    376 perslab  2788

slab class   8: chunk size    472 perslab  2221

slab class   9: chunk size    592 perslab  1771

slab class  10: chunk size    744 perslab  1409

 

可見, 組間差距比因子爲 2 時小得多, 更適合緩存幾百字節的記錄。從上面的輸出結果來看, 可能會以爲有些計算偏差,這些偏差是爲了保持字節數的對齊而故意設置的。

將 memcached 引入產品, 或是直接使用默認值進行部署時,最好是從新計算一下數據的預期平均長度,調整 growth factor,以得到最恰當的設置。

內存是珍貴的資源, 浪費就太惋惜了

 

4 memcached 的刪除機制

1)memcached是緩存,因此數據不會永久保存在服務器上,其實數據不會真正從memcached中消失。

   實際上memcached不會釋放已分配的內存,記錄超時後,客戶端就沒法再看見該記錄,其存儲空間便可重複使用。

2)Lazy Expiration:memcached內部不會監視記錄是否過時,而是在get時查看記錄的時間戳,檢查記錄是否過時。

  這種技術被稱爲lazy(惰性)expiration。所以,memcached不會在過時監視上耗費CPU時間。

3)memcached會優先使用已超時的記錄的空間,但即便如此,也會發生追加新記錄時空間不足的狀況,此時就要使用名爲 Least Recently Used(LRU)機制來分配空間。 

   顧名思義,這是刪除「最近最少使用」的記錄的機制。所以,當memcached的內存空間不足時(沒法從slab class 獲取到新的空間時),

   就從最近未被使用的記錄中搜索,並將其空間分配給新的記錄。

 

memcached 啓動時經過「 ­M」參數能夠禁止 LRU,以下所示:

$ memcached ­M ­m 1024

啓動時必須注意的是, 小寫的「 ­m」選項是用來指定最大內存大小的。不指定具體數值則使用默認值 64MB。

指定「 ­M」參數啓動後,內存用盡時 memcached 會返回錯誤。 話說回來, memcached 畢竟不是存儲器,而是緩存, 因此推薦使用 LRU。

 

  •  一些重要的特性

1 memcached如何實現冗餘機制? 

不實現!

Memcached應該是應用的緩存層。它的設計自己就不帶有任何冗餘機制。

若是一個memcached節點失去了全部數據,您應該從數據源(好比數據庫)再次獲取到數據。您應該特別注意,您的應用應該能夠容忍節點的失效。

不要寫一些糟糕的查詢代碼,寄但願於memcached來保證一切!若是您擔憂節點失效會大大加劇數據庫的負擔,那麼您能夠採起一些辦法。

好比您能夠增長更多的節點(來減小丟失一個節點的影響),熱備節點(在其餘節點down了的時候接管IP),等等。

 

2 memcached如何處理容錯的? 

不處理!:) 

在memcached節點失效的狀況下,集羣沒有必要作任何容錯處理。若是發生了節點失效,應對的措施徹底取決於用戶。節

點失效時,下面列出幾種方案供您選擇:

 

忽略它! 在失效節點被恢復或替換以前,還有不少其餘節點能夠應對節點失效帶來的影響。

把失效的節點從節點列表中移除。作這個操做千萬要當心!在默認狀況下(餘數式哈希算法),客戶端添加或移除節點,會致使全部的緩存數據不可用!

由於哈希參照的節點列表變化了,大部分key會由於哈希值的改變而被映射到(與原來)不一樣的節點上。

 

啓動熱備節點,接管失效節點所佔用的IP。這樣能夠防止哈希紊亂(hashing chaos)。

 

若是但願添加和移除節點,而不影響原先的哈希結果,可使用一致性哈希算法(consistent hashing)。

您能夠百度一下一致性哈希算法。支持一致性哈希的客戶端已經很成熟,並且被普遍使用。去嘗試一下吧!

 

兩次哈希(reshing)。當客戶端存取數據時,若是發現一個節點down了,就再作一次哈希(哈希算法與前一次不一樣),從新選擇另外一個節點(須要注意的時,客戶端並無把down的節點從節點列表中移除,下次仍是有可能先哈希到它)。若是某個節點時好時壞,兩次哈希的方法就有風險了,好的節點和壞的節點上均可能存在髒數據(stale data)。

 

 

3 memcached是如何作身份驗證的? 

 

沒有身份認證機制!

memcached是運行在應用下層的軟件(身份驗證應該是應用上層的職責)。

memcached的客戶端和服務器端之因此是輕量級的,部分緣由就是徹底沒有實現身份驗證機制。

這樣,memcached能夠很快地建立新鏈接,服務器端也無需任何配置。

 

 4 memcached能接受的key的最大長度是多少? 

key的最大長度是250個字符。

須要注意的是,250是memcached服務器端內部的限制,若是您使用的客戶端支持"key的前綴"或相似特性,

那麼key(前綴+原始key)的最大長度是能夠超過250個字符的。咱們推薦使用使用較短的key,由於能夠節省內存和帶寬。

 

5 memcached最大能存儲多大的單個item? 
 1MB。若是你的數據大於1MB,能夠考慮在客戶端壓縮或拆分到多個key中。

 

6爲何單個item的大小被限制在1M byte以內? 

  簡單的回答:由於內存分配器的算法就是這樣的。

  詳細的回答:Memcached的內存存儲引擎(引擎未來可插拔...),使用slabs來管理內存。內存被分紅大小不等的slabs chunks(先分紅大小相等的slabs,而後每一個slab被分紅大小相等chunks,不一樣slab的chunk大小是不相等的)。chunk的大小依次從一個最小數開始,按某個因子增加,直到達到最大的可能值。

  若是最小值爲400B,最大值是1MB,因子是1.20,各個slab的chunk的大小依次是:slab1 - 400B slab2 - 480B slab3 - 576B ...

  slab中chunk越大,它和前面的slab之間的間隙就越大。所以,最大值越大,內存利用率越低。Memcached必須爲每一個slab預先分配內存,所以若是設置了較小的因子和較大的最大值,會須要更多的內存。

  還有其餘緣由使得您不要這樣向memcached中存取很大的數據...不要嘗試把巨大的網頁放到mencached中。把這樣大的數據結構load和unpack到內存中須要花費很長的時間,從而致使您的網站性能反而很差。

  若是您確實須要存儲大於1MB的數據,你能夠修改slabs.c:POWER_BLOCK的值,而後從新編譯memcached;或者使用低效的malloc/free。其餘的建議包括數據庫、MogileFS等。

相關文章
相關標籤/搜索