簡單理解memcached的記憶體分配
在寫完ofollow,noindex">《使用Memcached實現抽獎活動》 這篇文章後,發現自己雖然很早就使用過 Memcached,但已經很久沒有關注它的進展了,所以就全面看了下它的官方 Wiki,打算寫幾篇文章去理解它,今天就簡單聊聊它的記憶體分配。
Memcached 所有的操作都是在記憶體中進行的,這也是它高效能和延遲低的原因之一,如果使用 malloc() 機制動態的分配記憶體,會產生很多的記憶體碎片,所以 Memcached 是自己管理記憶體的,在啟動的時候就開啟了一大塊記憶體,然後根據規則進行分配使用,這種機制也稱為 Slab allocation。
在啟動的時候可以通過 -m 引數分配記憶體,這些記憶體都用於儲存 item,Memcached 自身還需要一些記憶體(比如 Hash 表,網路連線),所以實際上 Memcached 使用的所有記憶體是超過 -m 引數分配的記憶體。
現在簡單理解下 Slab allocation,在 Memcached 中,記憶體首先被分配給一個個 pages,每個 page 預設大小都是 1MB,假設 -m 啟動引數是 64 M(後面都以 64 M 說明),那麼系統就存在 64 個 page。
每個 page 被指定為 slab-class,slab-class 相當於每個 page 的屬性,未被指定 slab-class 的 page 就是未分配的記憶體。
slab-class 決定了每個 page 儲存 item 的大小,稱之為 chunk,chunnk 就是用來儲存 item 的,在同一個 page 中,所有的 chunk 大小是一致的。
不同的 slab-class 其 chunk 大小是不一致的,基本上是遞增關係,預設是 1.25 比例的增長方式。
為了理解 page,slab-class,chunk 的關係,可以執行下列命令:
$ memcached -vv slab class1: chunk size96 perslab10922 slab class2: chunk size120 perslab8738 slab class3: chunk size152 perslab6898 slab class4: chunk size192 perslab5461 slab class5: chunk size240 perslab4369 slab class6: chunk size304 perslab3449 slab class7: chunk size384 perslab2730 slab class8: chunk size480 perslab2184 slab class9: chunk size600 perslab1747 ...
slab class 1 其 chunk 大小是 96 位元組,那麼對應的 page 最多可以儲存 10922 個 chunk。slab class 2 根據 slab class 1 * 1.25 的公示,chunk 大小是 120 個位元組,對應的 page 最多可以儲存 8738 個 chunk,依次類推。
需要注意的是:
- 假設 -m 是 64 M,不代表 64 個 page 全部被分配了,因為如果儲存的 item 很少,很多 page 是未分配的。
- 不同的 page,其 slab-class 可能是一樣的,比如所有的 item 都小於 80 位元組,一旦第一個 page 儲存滿了,那麼 Memcached 啟用第二個 page 的時候,其仍然被指定為 slab-class 2。
- 如果有個 item 是 1M,那麼這個 slab-class 只能儲存 1 個 chunk。
每個 slab-class 都是獨立的,有獨立的 LRU 演算法(後面介紹),統計資料也是獨立的。
如果想了解下 slab-class 的使用情況,可以輸入以下命令:
$ stats slabs STAT 1:chunk_size 96 STAT 1:chunks_per_page 10922 STAT 1:total_pages 1 STAT 1:total_chunks 10922 STAT 1:used_chunks 109 STAT 1:free_chunks 10813 STAT 1:get_hits 36 STAT 1:cmd_set 109 STAT 2:chunk_size 120 STAT 2:chunks_per_page 17476 STAT 2:total_pages 2 STAT 2:total_chunks 17476
可以看出目前 Memcached 分配了 3 個 page,對應 slab-class1 和 slab-class2。第一個 page 用了 109 個 chunk,gets 請求有 36 個,很可惜沒有 gets_miss 的統計資料,無法統計 slab-class 的命中率。
如果想了解 item 的詳細使用情況,可以輸入下列命令:
$ stats items STAT items:1:number 108 STAT items:1:number_hot 20 STAT items:1:number_warm 0 STAT items:1:number_cold 88 STAT items:1:age_hot 251 STAT items:1:age_warm 0 STAT items:1:age 67139 STAT items:1:evicted 0 STAT items:1:evicted_nonzero 0
其中涉及了很多 LRU 的統計資料,以及命中率資料,這個後續會講。
那麼 chunk size 如果等於 96 個位元組(slab-class1),代表可以儲存 96 個位元組的 item?並不是,每個 item 包含固定資料結構大小、item 的 key,item 對應的 data,如果要了解每個 item 資料結構的大小,可以下載 Memcached 原始檔,然後進入根目錄,輸入如下命令:
$ ./sizes Settings248 Item (no cas)48 Item (cas)56
如果 Memcached 要支援 CAS 操作(如果不瞭解 CAS,參考《使用Memcached實現抽獎活動》 ),每個 item 的資料結構是 56 位元組,也就是對於 slab-class1 來說,實際可以儲存的 item 是 40 個位元組,slab-class2 可以儲存的 item 是 64 個位元組。
如果儲存的 item(包含資料結構)是 80 位元組,那麼儲存在 slab-class1,但浪費了 16 個位元組,如果儲存的 item 是 140 個位元組,只能儲存在 slab-class2 中,浪費的位元組更多,這就是它的記憶體分配規則。
如果對你儲存的 item 大小比較瞭解(比如大部分處於 60-140個位元組之間),為了節省記憶體,可以調整 slab-class 的遞增因子,啟動的時候輸入下列命令:
$ memcached -f 1.1