Memcache記憶體分配原理介紹
1 Memcached簡介
1.1 什麼是Memcached
Memcached是一個高效能的分散式記憶體物件快取系統,用於動態Web應用以減輕資料庫負載。它通過在記憶體中快取資料和物件來減少讀取資料庫的次數,從而提供動態、資料庫驅動網站的速度。Memcached基於一個儲存鍵/值對的hashmap。其守護程序(daemon )是用C寫的,但是客戶端可以用任何語言來編寫,並通過memcached協議與守護程序通訊。
Memcached是免費並且開源的。
1.2 Memcached主頁
http://memcached.org/
1.3 Memcached版本
最新版本為V1.4.15
2013-09-03 釋出
1.4 Memcached License
BSD license
1.5 Memcached使用者
LiveJournal
Wikipedia
Flickr
Bebo
Typepad
Yellowbot
Youtube
Digg
WordPress.com
Craigslist
Mixi
2 memcached的特徵
memcached作為高速執行的分散式快取伺服器,具有以下的特點。
- 協議簡單
- 基於libevent的事件處理
- 內建記憶體儲存方式
- memcached不互相通訊的分散式
2.1 協議簡單
memcached的伺服器客戶端通訊並不使用複雜的XML等格式, 而使用簡單的基於文字行的協議。因此,通過telnet 也能在memcached上儲存資料、取得資料。下面是例子。
$ telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
set foo 0 0 3 (儲存命令)
bar (資料)
STORED (結果)
get foo (取得命令)
VALUE foo 0 3 (資料)
bar (資料)
2.2 基於libevent的事件處理
libevent是個程式庫,它將Linux的epoll、BSD類作業系統的kqueue等事件處理功能 封裝成統一的介面。即使對伺服器的連線數增加,也能發揮O(1)的效能。 memcached使用這個libevent庫,因此能在Linux、BSD、Solaris等作業系統上發揮其高效能。 關於事件處理這裡就不再詳細介紹,可以參考Dan Kegel的The C10K Problem。
2.3 內建記憶體儲存方式
為了提高效能,memcached中儲存的資料都儲存在memcached內建的記憶體儲存空間中。 由於資料僅存在於記憶體中,因此重啟memcached、重啟作業系統會導致全部資料消失。 另外,內容容量達到指定值之後,就基於LRU(Least Recently Used)演算法自動刪除不使用的快取。 memcached本身是為快取而設計的伺服器,因此並沒有過多考慮資料的永久性問題。 關於記憶體儲存的詳細資訊,本連載的第二講以後前阪會進行介紹,請屆時參考。
2.4 memcached不互相通訊的分散式
memcached儘管是“分散式”快取伺服器,但伺服器端並沒有分散式功能。 各個memcached不會互相通訊以共享資訊。那麼,怎樣進行分散式呢? 這完全取決於客戶端的實現。本連載也將介紹memcached的分散式。
3 Memcached記憶體分配
Memcached預設情況下采用了名為Slab Allocator的機制分配、管理記憶體。 在該機制出現以前,記憶體的分配是通過對所有記錄簡單地進行malloc和free來進行的。 但是,這種方式會導致記憶體碎片,加重作業系統記憶體管理器的負擔,最壞的情況下, 會導致作業系統比memcached程序本身還慢。Slab Allocator就是為解決該問題而誕生的。
3.1 Slab Allocation的術語
Page
分配給Slab的記憶體空間,預設是1MB。分配給Slab之後根據slab的大小切分成chunk。
Ø Chunk
用於快取記錄的記憶體空間。
Ø Slab (Slab Class)
特定大小的chunk的組。
3.2 Slab Allocation機制
Slab Allocation的原理相當簡單。 將分配的記憶體(page)分割成各種尺寸的塊(chunk), 並把尺寸相同的塊分成組(Slab,即chunk的集合)
Memcached的記憶體分配策略就是:按slab需求分配page,各slab按需使用chunk儲存。
這裡有幾個特點要注意,
1). Memcached分配出去的page不會被回收或者重新分配
2). Memcached申請的記憶體不會被釋放
3). slab空閒的chunk不會借給任何其他slab使用
3.2.1 Page為記憶體分配的最小單位
Memcached的記憶體分配以page為單位,預設情況下一個page是1M,可以通過-I引數在啟動時指定。如果需要申請記憶體時,memcached會劃分出一個新的page並分配給需要的slab區域。page一旦被分配在重啟前不會被回收或者重新分配(page ressign已經從1.2.8版移除了)
3.2.2 Slabs劃分資料空間
Memcached並不是將所有大小的資料都放在一起的,而是預先將資料空間劃分為一系列slabs,每個slab只負責一定範圍內的資料儲存。如下圖,每個slab只儲存大於其上一個slab的size並小於或者等於自己最大size的資料。例如:slab 3只儲存大小介於137 到 224 bytes的資料。如果一個數據大小為230byte將被分配到slab 4中。從下圖可以看出,每個slab負責的空間其實是不等的,memcached預設情況下下一個slab的最大值為前一個的1.25倍,這個可以通過修改-f引數來修改增長比例。
Slab Class 示意圖:
3.2.3 Chunk 才是儲存資料的單位
Chunk是一系列固定的記憶體空間,這個大小就是管理它的slab的最大存放大小。例如:slab 1的所有chunk都是104byte,而slab 4的所有chunk都是280byte。chunk是memcached實際存放快取資料的地方,因為chunk的大小固定為slab能夠存放的最大值,所以所有分配給當前slab的資料都可以被chunk存下。如果時間的資料大小小於chunk的大小,空餘的空間將會被閒置,這個是為了防止記憶體碎片而設計的。例如下圖,chunk size是224byte,而儲存的資料只有200byte,剩下的24byte將被閒置。
3.2.4 Page Slab Chunk 三者的關係
3.3 Slab中快取記錄的原理
下面說明memcached如何針對客戶端傳送的資料選擇slab並快取到chunk中。
memcached根據收到的資料的大小,選擇最適合資料大小的slab(圖2)。 memcached中儲存著slab內空閒chunk的列表,根據該列表選擇chunk,然後將資料緩存於其中。
3.4 Slab Allocator的缺點
Slab Allocator解決了當初的記憶體碎片問題,但新的機制也給memcached帶來了新的問題。
這個問題就是,由於分配的是特定長度的記憶體,因此無法有效利用分配的記憶體。例如,將100位元組的資料快取到128位元組的chunk中,剩餘的28位元組就浪費了。
對於該問題目前還沒有完美的解決方案,但在文件中記載了比較有效的解決方案。
3.5 使用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的利用率等各種各樣的資訊。
3.6 檢視memcached的內部狀態
memcached有個名為stats的命令,使用它可以獲得各種各樣的資訊。執行命令的方法很多,用telnet最為簡單:
$ telnet 主機名 埠號
連線到memcached之後,輸入stats再按回車,即可獲得包括資源利用率在內的各種資訊。 此外,輸入"stats slabs"或"stats items"還可以獲得關於快取記錄的資訊。 結束程式請輸入quit。
這些命令的詳細資訊可以參考memcached軟體包內的protocol.txt文件。
各項引數的解釋:
pid memcache伺服器的程序ID
uptime 伺服器已經執行的秒數
time 伺服器當前的unix時間戳
version memcache版本
pointer_size 當前OS的指標大小(32位系統一般是32bit)
rusage_user 程序的累計使用者時間
rusage_system 程序的累計系統時間
curr_items 伺服器當前儲存的items數量
total_items 從伺服器啟動以後儲存的items總數量
bytes 當前伺服器儲存items佔用的位元組數
curr_connections 當前開啟著的連線數
total_connections 從伺服器啟動以後曾經開啟過的連線數
connection_structures 伺服器分配的連線構造數
cmd_get get命令(獲取)總請求次數
cmd_set set命令(儲存)總請求次數
get_hits 總命中次數
get_misses 總未命中次數
evictions 為獲取空閒記憶體而刪除的items數(分配給memcache的空間用滿後需要刪除舊的items來得到空間分配給新的items)
bytes_read 總讀取位元組數(請求位元組數)
bytes_written 總髮送位元組數(結果位元組數)
limit_maxbytes 分配給memcache的記憶體大小(位元組)
threads 當前執行緒數
3.7 檢視slabs的使用狀況
列 | 含義 |
# | slab class編號 |
Item_Size | Chunk大小 |
Max_age | LRU內最舊的記錄的生存時間 |
1MB_pages | 分配給Slab的頁數 |
Count | Slab內的記錄數 |
Full? | Slab內是否含有空閒chunk |
4 Memcached記憶體複用
4.1 資料不會真正從memcached中消失
memcached不會釋放已分配的記憶體。記錄超時後,客戶端就無法再看見該記錄(invisible,透明),其儲存空間即可重複使用。
memcached內部不會監視記錄是否過期,而是在get時檢視記錄的時間戳,檢查記錄是否過期。這種技術被稱為lazy(惰性)expiration。因此,memcached不會在過期監視上耗費CPU時間。
memcached會優先使用已超時的記錄的空間,但即使如此,也會發生追加新記錄時空間不足的情況,此時就要使用名為 Least Recently Used(LRU)機制來分配空間。 顧名思義,這是刪除“最近最少使用”的記錄的機制。 因此,當memcached的記憶體空間不足時(無法從slab class 獲取到新的空間時),就從最近未被使用的記錄中搜索,並將其空間分配給新的記錄。 從快取的實用角度來看,該模型十分理想。
但如果在監控Memcached發現,evictions 不為0的時候,說明LRU已經開始顯現作用了,已經有部分item被重寫掉了。如果大量資訊被重寫掉了,需要看一下是否設定的memcached的記憶體太小!
不過,有些情況下LRU機制反倒會造成麻煩。memcached啟動時通過“-M”引數可以禁止LRU,如下所示:
$ memcached -M -m 1024
啟動時必須注意的是,小寫的“-m”選項是用來指定最大記憶體大小的。不指定具體數值則使用預設值64MB。
指定“-M”引數啟動後,記憶體用盡時memcached會返回錯誤。但memcached畢竟不是儲存器,而是快取,所以推薦使用LRU。
相關推薦
Memcache記憶體分配原理介紹
1 Memcached簡介 1.1 什麼是Memcached Memcached是一個高效能的分散式記憶體物件快取系統,用於動態Web應用以減輕資料庫負載。它通過在記憶體中快取資料和物件來減少讀取資料庫的次數,從而提供動態、資料庫驅動網站的速度。Memcache
簡單易懂的 Go 記憶體分配原理解讀
1. 前言 編寫過C語言程式的肯定知道通過malloc()方法動態申請記憶體,其中記憶體分配器使用的是glibc提供的ptmalloc2。 除了glibc,業界比較出名的記憶體分配器有Google的tcmalloc和Facebook的jemalloc。二者在避免記憶體碎片和效能上均比glic有比較大的優勢,
malloc記憶體分配原理
一、malloc的工作機制 它有一個將可用的記憶體塊連線為一個長長的列表的所謂空閒連結串列。 呼叫malloc函式時,它沿連線表尋找一個大到足以滿足使用者請求所需要的記憶體塊。然後,將該記憶體塊一分為二(一塊的大小與使用者請求的大小相等,另一塊的大小就是剩下的位元組)。接下來,將分配給
Memcache 記憶體分配策略
memcached預設採用了名為Slab Allocator的機制分配和管理記憶體。 在該機制出現以前,記憶體的分配是通過對所有記錄簡單的進行malloc和free來進行了。但是這種方式會導致記憶體碎片化嚴重,加重作業系統記憶體管理器的負擔。Slab Alloc
Java關鍵字new-----物件的記憶體分配原理
一、關鍵字new概述 "new"可以說是Java開發者最常用的關鍵字,我們使用new建立物件,使用new並通過類載入器來例項化任何我們需要的東西,但你是否深入瞭解過new在編譯的瞬間都做了什麼? 在Java中使用new關鍵字建立物件變得很容
java虛擬機器記憶體分配原理概述
本文主要介紹在應用發起記憶體申請,到作業系統最終分配記憶體,採用了那些途徑和方法,並比較各種方法的優劣以及使用過程中應該注意那些點。 注意本文都是概述,如想詳細瞭解,需單獨詳細瞭解每一部分內容 1、應用在那些情況下發起記憶體申請 2、記憶體發起申請的步驟(
淺談 java堆疊和記憶體分配原理
在java中我們把java記憶體分為兩種一種是棧記憶體,一種則是堆記憶體 1.在談java堆疊知識之前我們先來看看java虛擬機器的自動垃圾回收機制 引用變數是普通的變數,定義時在棧中分配,引用變數在程式執行到其作用域之外後被釋放。而陣列和物件本身在堆中
JAVA中堆疊和記憶體分配原理
2、Java記憶體分配中的棧 在函式中定義的一些基本型別的變數資料和物件的引用變數都在函式的棧記憶體中分配。 當在一段程式碼塊定義一個變數時,Java在棧中為這個變數分配記憶體空間,當該變數退出其作用域後,Java會自動釋放掉為該變數所分配的記憶體空間,該記憶體空間可以立即被另作他用。 Java記憶
【Linux應用開發】malloc記憶體分配原理
如何檢視程序發生缺頁中斷的次數? 用ps -o majflt,minflt -C program命令檢視。 majflt代表major fault,中文名叫
Java記憶體區域劃分、記憶體分配原理(基於jdk1.7 源自 《深入理解java虛擬機器》)
執行時資料區域 Java虛擬機器在執行Java的過程中會把管理的記憶體劃分為若干個不同的資料區域。這些區域有各自的用途,以及建立和銷燬的時間,有的區域隨著虛擬機器程序的啟動而存在,而有的區域則依賴執行緒的啟動和結束而建立和銷燬。 Java虛擬機
深入理解golang:記憶體分配原理
## 一、Linux系統記憶體 在說明golang記憶體分配之前,先了解下Linux系統記憶體相關的基礎知識,有助於理解golang記憶體分配原理。 ### 1.1 虛擬記憶體技術 在早期記憶體管理中,如果程式太大,超過了空閒記憶體容量,就沒有辦法把全部程式裝入到記憶體,這時怎麼辦? 在許多年前,人們採
malloc動態記憶體分配機制原理_及_linux/proc/介紹
程序系統資源的使用原理 大部分程序通過glibc申請使用記憶體,但是glibc也是一個應用程式庫,它最終也是要呼叫作業系統的記憶體管理介面來使用記憶體。大部分情況下,glibc對使用者和作業系統是透
memcache原理1.5.8——記憶體分配與淘汰
本文主要參考:好,下面上貨。首先需要了解一下memcache是如何進行記憶體管理的。記憶體分配首先通過命令列的-m引數給資料預留記憶體。然後記憶體會按照預設每頁1M大小分配給需要的slab class。然後這1M記憶體根據需要配切分成指定大小的chunks。然後看一下啟動me
Linux夥伴系統原理-記憶體分配和釋放
主要分析Linux夥伴系統演算法,記憶體的分配和釋放 1.夥伴系統簡介 Linux核心記憶體管理的一項重要工作就是如何在頻繁申請釋放記憶體的情況下,避免碎片的產生, Linux採用夥伴系統解決外部碎片的問題,採用slab解決內 部碎片的問
java建立物件記憶體分配空間及其原理一
一直想寫關於java物件的文章,一直拖著就等到了現在。其實,當你真正走上程式設計師這條道路的正軌時,程式碼對於我們來說,已經不再是問題了。但是,假如我問你原理,你真的能知道其一二嗎?
JVM初探- 記憶體分配、GC原理與垃圾收集器
JVM記憶體的分配與回收大致可分為如下4個步驟: 何時分配 -> 怎樣分配 -> 何時回收 -> 怎樣回收. 除了在概念上可簡單認為new時分配外, 我們著重介紹後面的3個步驟: I. 怎樣分配- JVM記憶體分配策略 物件記憶體主要分配
memcache記憶體池的設計原理
memcache中管理記憶體的資料結構如下: typedef struct { unsigned int size; /* sizes of items */ unsigned int perslab; /* how many items per slab */ vo
作業系統原理:動態記憶體分配
動態記憶體分配背後的機制深刻的體現了電腦科學中的這句名言: All problem in CS can be solved by another level of indirection. — Butler Lampson
全面介紹Windows記憶體管理機制及C++記憶體分配例項
本文基本上是windows via c/c++上的內容,筆記做得不錯。。 本文背景: 在程式設計中,很多Windows或C++的記憶體函式不知道有什麼區別,更別談有效使用;根本的原因是,沒有清楚的理解作業系統的記憶體管理機制,本文企圖通過簡單的總結描述,結合例
Java虛擬機器原理、記憶體分配和回收機制
通常情況下Java編譯過的程式碼是一些class檔案,Java虛擬機器在執行程式碼的時候,首先解析Class,查詢該類的方法、常量,這些對於常規情況下都編譯成二進位制的程式碼儲存在jar檔案中,而對於Java的反射,VM的類載入器需要動態的查詢這些類名,雖然節省了編譯時間,但是執行時的查詢大大降低執行效率。