1. 程式人生 > >memcached的學習(6)

memcached的學習(6)

2018.6.13
找到了一篇部落格,全面解析memcached的知識點,主要從伺服器端的記憶體原理,客戶端的分散式原理,以及擴充套件方向來介紹的,下面對這幾個方面做一個總結:

1、理解memcached的記憶體儲存

我是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)。
在這裡插入圖片描述

而且,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, 然後將資料緩存於其中。
在這裡插入圖片描述

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

Slab Allocator的缺點

Slab Allocator解決了當初的記憶體碎片問題,但新的機制也給memcached帶來了新的問題。
這個問題就是,由於分配的是特定長度的記憶體,因此無法有效利用分配的記憶體。 例如,將100位元組的資料快取到128位元組的chunk中,剩餘的28位元組就浪費了(圖3)。
在這裡插入圖片描述

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

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可以從下面的地址獲得:
http://tangent.org/552/libmemcached.html

檢視slabs的使用狀況

使用memcached的創造著Brad寫的名為memcached-tool的Perl指令碼,可以方便地獲得slab的使用情況 (它將memcached的返回值整理成容易閱讀的格式)。可以從下面的地址獲得指令碼:
http://code.sixapart.com/svn/memcached/trunk/server/scripts/memcached-tool

使用方法也極其簡單:

$ 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_Size Chunk大小
Max_age LRU內最舊的記錄的生存時間
1MB_pages 分配給Slab的頁數
Count Slab內的記錄數
Full Slab內是否含有空閒chunk

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

記憶體儲存的總結

memcached是快取,所以資料不會永久儲存在伺服器上,這是向系統中引入memcached的前提。 本次介紹memcached的資料刪除機制,以及memcached的最新發展方向——二進位制協議(Binary Protocol) 和外部引擎支援。

memcached在資料刪除方面有效利用資源
資料不會真正從memcached中消失

上次介紹過, memcached不會釋放已分配的記憶體。記錄超時後,客戶端就無法再看見該記錄(invisible,透明), 其儲存空間即可重複使用。

Lazy Expiration

memcached內部不會監視記錄是否過期,而是在get時檢視記錄的時間戳,檢查記錄是否過期。 這種技術被稱為lazy(惰性)expiration。因此,memcached不會在過期監視上耗費CPU時間。
LRU:從快取中有效刪除資料的原理

memcached會優先使用已超時的記錄的空間,但即使如此,也會發生追加新記錄時空間不足的情況, 此時就要使用名為 Least Recently Used(LRU)機制來分配空間。 顧名思義,這是刪除“最近最少使用”的記錄的機制。 因此,當memcached的記憶體空間不足時(無法從slab class 獲取到新的空間時),就從最近未被使用的記錄中搜索,並將其空間分配給新的記錄。 從快取的實用角度來看,該模型十分理想。

不過,有些情況下LRU機制反倒會造成麻煩。memcached啟動時通過“-M”引數可以禁止LRU,如下所示:

$ memcached -M -m 1024

啟動時必須注意的是,小寫的“-m”選項是用來指定最大記憶體大小的。不指定具體數值則使用預設值64MB。
指定“-M”引數啟動後,記憶體用盡時memcached會返回錯誤。 話說回來,memcached畢竟不是儲存器,而是快取,所以推薦使用LRU。