網(wǎng)上其實有很多文章說明了memcached是如何運作的,特別是底層的內(nèi)存分配是如何運作的。我參考過很多資料,比較有啟發(fā)意義的有幾個:
- 首先是官方的英文資料,雖然文章太多、很難看懂,我個人覺得說得也不是太清晰,不夠直觀(附上 官方wiki地址 )。還有g(shù)it上memcached原作者的 protocol.txt 也值得一讀,有時間的話,我會翻譯一下。
- 其次是mixi網(wǎng)站的核心技術(shù)人員寫的5篇全面剖析,這5篇文章寫得很實用,從各方面徹底解剖了memcached,可惜的是這一系列文章對于核心的內(nèi)存分配邏輯描述還不夠詳細(xì)(個人覺得),而且很多監(jiān)控方面的guide都是基于mixi站點已經(jīng)有的一套框架,對于從頭開發(fā)的人來說只能是用來指明方向的,實際指導(dǎo)意義不大(附上我轉(zhuǎn)載的帖子 入口 )。
- 此外還有Mike Perham的 一篇博客 ,寫得不錯,很詳細(xì)地舉例說明了memcached內(nèi)部的內(nèi)存分配邏輯,以及內(nèi)存浪費現(xiàn)象。
- 還有梁子的 一篇博客 ,也是一篇很好的博客,從他的角度非常詳細(xì)地描述了memcached內(nèi)部的內(nèi)存分配邏輯。我個人覺得他的文章解釋的是最清楚的,雖然在文筆和錯別字上實在是。。。。
1. 幾個關(guān)鍵概念
Page為內(nèi)存分配的單位
Memcached的內(nèi)存分配以page為單位,默認(rèn)情況下一個page是1M,可以通過-I參數(shù)修改,最小1K,最大128M。如果需要申請內(nèi)存時,memcached會劃分出一個新的page并分配給需要的slab區(qū)域。page一旦被分配在memcached重啟前不會被回收或者重新分配(page ressign已經(jīng)從1.2.8版移除了)。
Slabs劃分?jǐn)?shù)據(jù)空間
Memcached并不是將所有大小的數(shù)據(jù)都放在一起的,而是預(yù)先將數(shù)據(jù)空間劃分為一系列大小的slabs,每個slab只負(fù)責(zé)一定大小范圍內(nèi)的數(shù)據(jù)存儲。每個slab只存儲大于其上一個slab的size并小于或者等于自己最大size的數(shù)據(jù)。例如:slab 3只存儲大小介于137 到 224 bytes的數(shù)據(jù)。如果一個數(shù)據(jù)大小為230byte的數(shù)據(jù)進行存儲,它將被分配到slab 4中。每個slab負(fù)責(zé)的空間其實是不等的,memcached默認(rèn)情況下下一個slab的最大值為前一個的1.25倍,這個可以通過修改-f參數(shù)來修改增長比例。
Chunk才是存放緩存數(shù)據(jù)的單位
Chunk是一系列固定的內(nèi)存空間,這個大小就是管理它的slab的最大存放大小。例如:slab 1的所有chunk都是104byte,而slab 4的所有chunk都是280byte。chunk是memcached實際存放緩存數(shù)據(jù)的地方,因為chunk的大小固定為slab能夠存放的最大值,所以所有分配給當(dāng)前slab的數(shù)據(jù)都可以被chunk存下。如果實際的數(shù)據(jù)大小小于chunk的大小,空余的空間將會被閑置,這個是為了防止內(nèi)存碎片而設(shè)計的。舉例來說,如果chunk size是224byte,而存儲的數(shù)據(jù)只有200byte,剩下的24byte將被閑置。此外,memcached允許配置的最小的chunk空間為48個字節(jié)(key+value+flags),通過-n參數(shù)可以調(diào)節(jié)這個數(shù)值。
2. 理解這三者之間的關(guān)系
要理解memcached是如何分配內(nèi)存的就要從理解上述三個東西之間的關(guān)系開始。
page
是memcached在收到內(nèi)存不夠的請求,并進行內(nèi)存分配的單位。舉例來說,slab2的所有空間都用完了,又有大小適合slab2的數(shù)據(jù)過來了,那么slab2就會向memcached請求新的內(nèi)存空間,memcached就會劃分一個page大小的內(nèi)存量到slab2。page的默認(rèn)大小是1M,這個數(shù)值可以通過參數(shù)-I來修改。
slab
是memcached用來劃定存儲空間的大小概念,每當(dāng)memcached啟動的時候,它會按照-n參數(shù)配置的值(如果有的話,否則為默認(rèn)值)來決定第一個slab的大小,然后根據(jù)-f參數(shù)的值來決定后續(xù)slab大小的增長速率,一個一個地決定后續(xù)的slab的大小,直到slab的大小達(dá)到設(shè)定的page大小(一般是1M)。
chunk
是實際用來存儲數(shù)據(jù)的內(nèi)存空間,它的大小和包含它的slab的大小是一致的。當(dāng)page大小的內(nèi)存分配到slab的時候,slab會根據(jù)自身的大小將page大小的內(nèi)存分割成 page / slabsize 個chunk。
memcached啟動時候,slab創(chuàng)建以及chunk分配的細(xì)節(jié)可以參照下面的數(shù)據(jù)(使用-vv命令查看的詳細(xì)內(nèi)存分配過程)。
/
usr
/
bin
/
memcached
-
u nobody
-
m
64
-
p
11211
-
l 127.0.0.1
-
vv
slab class
1
:
chunk size
96
perslab
10922
slab class
2
:
chunk size
120
perslab
8738
slab class
3
:
chunk size
152
perslab
6898
slab class
4
:
chunk size
192
perslab
5461
slab class
5
:
chunk size
240
perslab
4369
slab class
6
:
chunk size
304
perslab
3449
slab class
7
:
chunk size
384
perslab
2730
slab class
8
:
chunk size
480
perslab
2184
slab class
9
:
chunk size
600
perslab
1747
slab class
10
:
chunk size
752
perslab
1394
slab class
11
:
chunk size
944
perslab
1110
slab class
12
:
chunk size
1184
perslab
885
slab class
13
:
chunk size
1480
perslab
708
slab class
14
:
chunk size
1856
perslab
564
slab class
15
:
chunk size
2320
perslab
451
slab class
16
:
chunk size
2904
perslab
361
slab class
17
:
chunk size
3632
perslab
288
slab class
18
:
chunk size
4544
perslab
230
slab class
19
:
chunk size
5680
perslab
184
slab class
20
:
chunk size
7104
perslab
147
slab class
21
:
chunk size
8880
perslab
118
slab class
22
:
chunk size
11104
perslab
94
slab class
23
:
chunk size
13880
perslab
75
slab class
24
:
chunk size
17352
perslab
60
slab class
25
:
chunk size
21696
perslab
48
slab class
26
:
chunk size
27120
perslab
38
slab class
27
:
chunk size
33904
perslab
30
slab class
28
:
chunk size
42384
perslab
24
slab class
29
:
chunk size
52984
perslab
19
slab class
30
:
chunk size
66232
perslab
15
slab class
31
:
chunk size
82792
perslab
12
slab class
32
:
chunk size
103496
perslab
10
slab class
33
:
chunk size
129376
perslab
8
slab class
34
:
chunk size
161720
perslab
6
slab class
35
:
chunk size
202152
perslab
5
slab class
36
:
chunk size
252696
perslab
4
slab class
37
:
chunk size
315872
perslab
3
slab class
38
:
chunk size
394840
perslab
2
slab class
39
:
chunk size
493552
perslab
2
slab class
40
:
chunk size
616944
perslab
1
slab class
41
:
chunk size
771184
perslab
1
slab class
42
:
chunk size
1048576
perslab
1
?
3. 舉個例子來分析
首先,是memcached啟動時候的情況:
商人A很有錢,他有100個大小一摸一樣的倉庫(100M的memcached服務(wù)器,每個page大小1M,就是一個倉庫)。商人A根據(jù)自己的商品尺寸,將自己的倉庫分成了42種(42個slab),定義為最小一種的倉庫是專門用來存放尺寸為96的貨物的(slab1大小為96個字節(jié)),然后每種倉庫存放的貨物大小都是之前一種的1.25倍(增長因子-f為1.25)。商人預(yù)先將42個倉庫按照預(yù)定義的42種貨物大小整理、裝修了下(memcached啟動時候的42個slab預(yù)分配、chunk分割)。1號倉庫(slab1)中有10922個(1M * 1024 * 1024 / 96)貨物存儲空間(chunk),后續(xù)的倉庫類型的裝修、空間分配都以此類推。
其次,來看下slab滿了的時候的情況:
商人A進了一批尺寸是150的貨物,共6899個。貨物按大小分配,進入3號倉庫( slab3)。因為3號倉庫是倉庫類型3,其大小只有6898個位置(6898個chunk),6898個貨物被安置到倉庫類型3(slab3)的3號倉庫里去。然后還多出來一個貨物沒地方放,商人就安排了一個新的倉庫裝修成倉庫類型3(1M的空間分配給slab3,大小為152個字節(jié),含6898個chunk),然后將多余的一個貨物放入到新的倉庫里。
這個例子看過以后,相信大家都已經(jīng)很明白前述的三個概念之間的關(guān)系以及memcached是如何分配內(nèi)存空間的了。
4. memcached里的內(nèi)存浪費
讀過上文之后大家應(yīng)該很明白memcached的內(nèi)存分配方式了。memcached這樣分配內(nèi)存的好處是不會存在內(nèi)存碎片,但是壞處也很明顯,就是內(nèi)存的浪費。就拿前面的商人例子來說,如果遇到一種極端的情況,所有的貨物進來的都是121個字節(jié)的大小,那么按邏輯他們都會被分到slab3里面去,也就是分到大小是152的slab里,也就是說每塞進一個對象,就會有31個字節(jié)的內(nèi)存空間被浪費掉了。
5. memcached的數(shù)據(jù)回收機制
memcached內(nèi)部不會監(jiān)視記錄是否過期,而是在get時查看記錄的時間戳,檢查記錄是否過期。 這種技術(shù)被稱為lazy(惰性)expiration。因此,memcached不會在過期監(jiān)視上耗費CPU時間。如果某一個item在memcached里過期了,這個東西并不會被刪除,而是客戶端無法再看見該記錄(invisible,透明), 其存儲空間即可重復(fù)使用。一般情況下memcached會優(yōu)先使用已超時的記錄的空間,但即使如此,也會發(fā)生追加新記錄時空間不足的情況, 此時就要使用名為 Least Recently Used(LRU)機制來分配空間。 顧名思義,這是刪除“最近最少使用”的記錄的機制。 因此,當(dāng)memcached的內(nèi)存空間不足時(無法從slab class 獲取到新的空間時),就從最近未被使用的記錄中搜索,并將其空間分配給新的記錄。
以上,主要是memcached的內(nèi)存分配利用的一些經(jīng)驗。當(dāng)然,memcached的配置、調(diào)優(yōu)、監(jiān)控在這篇文章里是沒有涉及的,以后有機會的話會補上。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
