memcached全麵剖析–2.理解memcached的記憶體存儲

日期:2008-07-17  作者:喜騰小二  來源:PHPChina


版權宣告:可以任意轉載,但轉載時必須標明原作者charlee、原始連結http://tech.idv2.com/2008/07/11/memcached-002/以及本宣告。


下麵是《memcached全麵剖析》的第二部分。

發表日:2008/7/9
作者:前坂徹(Toru Maesaka)
原文連結:http://gihyo.jp/dev/feature/01/memcached/0002

  • Slab Allocation機制:整理記憶體以便重複使用
    • Slab Allocation的主要術語
  • 在Slab中快取記錄的原理
  • Slab Allocator的缺點
  • 使用Growth Factor進行調優
  • 檢視memcached的內部狀態
  • 檢視slabs的使用狀況
  • 記憶體存儲的總結

我是mixi株式會社研究開發組的前坂徹。 上次的文章介紹了memcached是分散式的高速快取伺服器。本次將介紹memcached的內部構造的實現方式,以及記憶體的管理方式。另外,memcached的內部構造導緻的弱點也將加以幫助。

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的集合)(圖1)。

memcached-0002-01.png

圖1 Slab Allocation的構造圖

而且,slab allocator還有重複使用已分配的記憶體的目的。也就是說,分配到的記憶體不會釋放,而是重複利用。

Slab Allocation的主要術語

Page

分配給Slab的記憶體空間,預設是1MB。分配給Slab之後根據slab的大小切分成chunk。

Chunk

用於快取記錄的記憶體空間。

Slab Class

特定大小的chunk的組。

在Slab中快取記錄的原理

下麵幫助memcached如何針對用戶端發送的資料選擇slab並快取到chunk中。

memcached根據收到的資料的大小,選擇最適合資料大小的slab(圖2)。 memcached中儲存着slab內空閒chunk的清單,根據該清單選擇chunk,然後將資料快取於其中。

memcached-0002-02.png

圖2 選擇存儲記錄的組的方法

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

Slab Allocator的缺點

Slab Allocator解決了當初的記憶體碎片問題,但新的機制也給memcached帶來了新的問題。

這個問題就是,由於分配的是特定長度的記憶體,因此無法有效利用分配的記憶體。例如,將100位元組的資料快取到128位元組的chunk中,剩餘的28位元組就浪費了(圖3)。

memcached-0002-03.png

圖3 chunk空間的使用

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

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 to store.

就是說,如果預先知道用戶端發送的資料的公用大小,或者僅快取大小相同的資料的情況下,只要使用適合資料大小的組的清單,就可以減少浪費。

但是很遺憾,現在還不能進行任何調優,隻能期待以後的版本了。但是,我們可以調節slab class的大小的差別。接下來幫助growth factor選項。

使用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,以獲得最恰當的設定。記憶體是珍貴的資源,浪費就太可惜了。

接下來介紹一下如何使用memcached的stats指令檢視slabs的利用率等各種各樣的資訊。

檢視memcached的內部狀態

memcached有個名為stats的指令,使用它可以獲得各種各樣的資訊。執行指令的方法很多,用telnet最為簡單:

$ telnet 主機名 連接埠號

連線到memcached之後,輸入stats再按回車,即可獲得包括資源利用率在內的各種資訊。此外,輸入"stats slabs"或"stats items"還可以獲得關於快取記錄的資訊。結束程式請輸入quit。

這些指令的詳細資訊可以參考memcached軟體包內的protocol.txt文檔。

$ telnet localhost 11211
Trying ::1...
Connected to localhost.
Escape character is '^]'.
stats
STAT pid 481
STAT uptime 16574
STAT time 1213687612
STAT version 1.2.5
STAT pointer_size 32
STAT rusage_user 0.102297
STAT rusage_system 0.214317
STAT curr_items 0
STAT total_items 0
STAT bytes 0
STAT curr_connections 6
STAT total_connections 8
STAT connection_structures 7
STAT cmd_get 0
STAT cmd_set 0
STAT get_hits 0
STAT get_misses 0
STAT evictions 0
STAT bytes_read 20
STAT bytes_written 465
STAT limit_maxbytes 67108864
STAT threads 4
END
quit

另外,如果安裝了libmemcached這個麵嚮C/C++語言的用戶端庫,就會安裝 memstat 這個惡指令。使用方法很簡單,可以用更少的步驟獲得與telnet相同的資訊,還能一次性從多臺伺服器獲得資訊。

$ memstat --servers=server1,server2,server3,...

libmemcached可以從下麵的地址獲得:

檢視slabs的使用狀況

使用memcached的創造着Brad寫的名為memcached-tool的Perl指令檔,可以方便地獲得slab的使用情況(它將memcached的返回值整理成容易閱讀的格式)。可以從下麵的地址獲得指令檔:

使用方法也極其簡單:

$ memcached-tool 主機名:連接埠 選項

檢視slabs使用狀況時無需指定選項,因此用下麵的指令即可:

$ memcached-tool 主機名:連接埠

獲得的資訊如下所示:

# Item_Size Max_age 1MB_pages Count Full?
1 104 B 1394292 s 1215 12249628 yes
2 136 B 1456795 s 52 400919 yes
3 176 B 1339587 s 33 196567 yes
4 224 B 1360926 s 109 510221 yes
5 280 B 1570071 s 49 183452 yes
6 352 B 1592051 s 77 229197 yes
7 440 B 1517732 s 66 157183 yes
8 552 B 1460821 s 62 117697 yes
9 696 B 1521917 s 143 215308 yes
10 872 B 1695035 s 205 246162 yes
11 1.1 kB 1681650 s 233 221968 yes
12 1.3 kB 1603363 s 241 183621 yes
13 1.7 kB 1634218 s 94 57197 yes
14 2.1 kB 1695038 s 75 36488 yes
15 2.6 kB 1747075 s 65 25203 yes
16 3.3 kB 1760661 s 78 24167 yes

各列的含義為:

含義
#slab class編號
Item_SizeChunk大小
Max_ageLRU內最舊的記錄的生存時間
1MB_pages分配給Slab的頁數
CountSlab內的記錄數
Full?Slab內是否含有空閒chunk

從這個指令檔獲得的資訊對於調優非常方便,強烈推薦使用。

記憶體存儲的總結

本次簡單幫助了memcached的快取機制和調優方法。希望讀者能理解memcached的記憶體管理原理及其優缺點。

下次將繼續幫助LRU和Expire等原理,以及memcached的最新發展方嚮—— 可擴充體係(pluggable architecher))。

<<<返回技術中心

技術文章

站內新聞