memcached是什嗎?
memcached 是以LiveJournal 旗下Danga Interactive 公司的Brad Fitzpatric 為首開發的一款軟體。現在已成為 mixi、 hatena、 Facebook、 Vox、LiveJournal等眾多服務中 提高Web應用擴充性的重要因素。
許多Web應用都將資料儲存到RDBMS中,應用伺服器從中讀取資料並在瀏覽器中顯示。 但隨著資料量的增大、訪問的集中,就會出現RDBMS的負擔加重、資料庫響應惡化、 網站顯示延遲等重大影響。
這時就該memcached大顯身手了。memcached是高效能的分布式記憶體快取服務器。 一般的使用目的是,通過快取資料庫查詢結果,減少資料庫訪問次數,以提高動態Web應用的速度、 提高可擴充性。
圖1 一般情況下memcached的用途
其實也不是很複雜,就是用軟體搭建起來的資料庫緩衝系統
memcached的特徵
memcached作為高速啟動並執行分布式快取服務器,具有以下的特點。
協議簡單
基於libevent的事件處理
內建記憶體儲存方式
memcached不互相通訊的分布式
協議簡單
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 (資料)
協議文檔位於memcached的原始碼內,也可以參考以下的URL。
http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
基於libevent的事件處理
libevent是個程式庫,它將Linux的epoll、BSD類作業系統的kqueue等事件處理功能 封裝成統一的介面。即使對伺服器的串連數增加,也能發揮O(1)的效能。 memcached使用這個libevent庫,因此能在Linux、BSD、Solaris等作業系統上發揮其高效能。 關於事件處理這裡就不再詳細介紹,可以參考Dan Kegel的The C10K Problem。
libevent: http://www.monkey.org/~provos/libevent/
The C10K Problem: http://www.kegel.com/c10k.html
內建記憶體儲存方式
為了提高效能,memcached中儲存的資料都儲存在memcached內建的記憶體儲存空間中。 由於資料僅存在於記憶體中,因此重啟memcached、重啟作業系統會導致全部資料消失。 另外,內容容量達到指定值之後,就基於LRU(Least Recently Used)演算法自動刪除不使用的緩衝。 memcached本身是為緩衝而設計的伺服器,因此並沒有過多考慮資料的永久性問題。 關於記憶體儲存的詳細資料,本連載的第二講以後前阪會進行介紹,請屆時參考。
memcached不互相通訊的分布式
memcached儘管是“分布式”快取服務器,但伺服器端並沒有分布式功能。 各個memcached不會互相通訊以共用資訊。那麼,怎樣進行分布式呢? 這完全取決於用戶端的實現。本連載也將介紹memcached的分布式。
圖2 memcached的分布式
接下來簡單介紹一下memcached的使用方法。
安裝memcached
memcached的安裝比較簡單,這裡稍加說明。
memcached支援許多平台。
Linux
FreeBSD
Solaris (memcached 1.2.5以上版本)
Mac OS X
另外也能安裝在Windows上。這裡使用Fedora Core 8進行說明。
memcached的安裝
運行memcached需要本文開頭介紹的libevent庫。Fedora 8中有現成的rpm包, 通過yum命令安裝即可。
$ sudo yum install libevent libevent-devel
memcached的原始碼可以從memcached網站上下載。本文執筆時的最新版本為1.2.5。 Fedora 8雖然也包含了memcached的rpm,但版本比較老。因為原始碼安裝並不困難, 這裡就不使用rpm了。
下載memcached:http://www.danga.com/memcached/download.bml
memcached安裝與一般應用程式相同,configure、make、make install就行了。
$ wget http://www.danga.com/memcached/dist/memcached-1.2.5.tar.gz
$ tar zxf memcached-1.2.5.tar.gz
$ cd memcached-1.2.5
$ ./configure
$ make
$ sudo make install
預設情況下memcached安裝到/usr/local/bin下。
memcached的啟動
從終端輸入以下命令,啟動memcached。
$ /usr/local/bin/memcached -p 11211 -m 64m -vv
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 38: chunk size 391224 perslab 2
slab class 39: chunk size 489032 perslab 2
<23 server listening
<24 send buffer was 110592, now 268435456
<24 server listening (udp)
<24 server listening (udp)
<24 server listening (udp)
<24 server listening (udp)
這裡顯示了調試資訊。這樣就在前台啟動了memcached,監聽TCP連接埠11211 最大記憶體使用量量為64M。調試資訊的內容大部分是關於儲存的資訊, 下次連載時具體說明。
作為daemon後台啟動時,只需
$ /usr/local/bin/memcached -p 11211 -m 64m -d
這裡使用的memcached啟動選項的內容如下。
參數說明
-p 使用的TCP連接埠。預設為11211
-m 最大記憶體大小。預設為64M
-vv 用very vrebose模式啟動,調試資訊和錯誤輸出到控制台
-d 作為daemon在後台啟動
上面四個是常用的啟動選項,其他還有很多,通過
$ /usr/local/bin/memcached -h
命令可以顯示。許多選項可以改變memcached的各種行為, 推薦讀一讀。
用用戶端串連
許多語言都實現了串連memcached的用戶端,其中以Perl、PHP為主。 僅僅memcached網站上列出的語言就有
Perl、PHP、Python、Ruby、C#、C/C++、Lua等等。
memcached用戶端API:http://www.danga.com/memcached/apis.bml
這裡介紹通過mixi正在使用的Perl庫連結memcached的方法。
使用Cache::Memcached
Perl的memcached用戶端有
Cache::Memcached
Cache::Memcached::Fast
Cache::Memcached::libmemcached
等幾個CPAN模組。這裡介紹的Cache::Memcached是memcached的作者Brad Fitzpatric的作品, 應該算是memcached的用戶端中應用最為廣泛的模組了。
Cache::Memcached - search.cpan.org: http://search.cpan.org/dist/Cache-Memcached/
使用Cache::Memcached串連memcached
下面的原始碼為通過Cache::Memcached串連剛才啟動的memcached的例子。
#!/usr/bin/perl
use strict;
use warnings;
use Cache::Memcached;
my $key = "foo";
my $value = "bar";
my $expires = 3600; # 1 hour
my $memcached = Cache::Memcached->new({
servers => ["127.0.0.1:11211"],
compress_threshold => 10_000
});
$memcached->add($key, $value, $expires);
my $ret = $memcached->get($key);
print "$ret\n";
在這裡,為Cache::Memcached指定了memcached伺服器的IP地址和一個選項,以產生執行個體。 Cache::Memcached常用的選項如下所示。
選項說明
servers 用數組指定memcached伺服器和連接埠
compress_threshold 資料壓縮時使用的值
namespace 指定添加到鍵的首碼
另外,Cache::Memcached通過Storable模組可以將Perl的複雜資料序列化之後再儲存, 因此散列、數組、對象等都可以直接儲存到memcached中。
儲存資料
向memcached儲存資料的方法有
add
replace
set
它們的使用方法都相同:
my $add = $memcached->add( '鍵', '值', '期限' );
my $replace = $memcached->replace( '鍵', '值', '期限' );
my $set = $memcached->set( '鍵', '值', '期限' );
向memcached儲存資料時可以指定期限(秒)。不指定期限時,memcached按照LRU演算法儲存資料。 這三個方法的區別如下:
選項說明
add 僅當儲存空間中不存在鍵相同的資料時才儲存
replace 僅當儲存空間中存在鍵相同的資料時才儲存
set 與add和replace不同,無論何時都儲存
擷取資料
擷取資料可以使用get和get_multi方法。
my $val = $memcached->get('鍵');
my $val = $memcached->get_multi('鍵1', '鍵2', '鍵3', '鍵4', '鍵5');
一次取得多條資料時使用get_multi。get_multi可以非同步地同時取得多個索引值, 其速度要比迴圈調用get快數十倍。
刪除資料
刪除資料使用delete方法,不過它有個獨特的功能。
$memcached->delete('鍵', '阻塞時間(秒)');
刪除第一個參數指定的鍵的資料。第二個參數指定一個時間值,可以禁止使用同樣的鍵儲存新資料。 此功能可以用於防止快取資料的不完整。但是要注意,set函數忽視該阻塞,照常儲存資料
增一和減一操作
可以將memcached上特定的索引值作為計數器使用。
my $ret = $memcached->incr('鍵');
$memcached->add('鍵', 0) unless defined $ret;
增一和減一是原子操作,但未設定初始值時,不會自動賦成0。因此, 應當進行錯誤檢查,必要時加入初始化操作。而且,伺服器端也不會對 超過2 32時的行為進行檢查。
總結
這次簡單介紹了memcached,以及它的安裝方法、Perl用戶端Cache::Memcached的用法。 只要知道,memcached的使用方法十分簡單就足夠了。
下次由前阪來說明memcached的內部結構。瞭解memcached的內部構造, 就能知道如何使用memcached才能使Web應用的速度更上一層樓。
More
理解memcached的記憶體儲存:http://tech.idv2.com/2008/07/11/memcached-002/
memcached的刪除機制和發展方向:http://tech.idv2.com/2008/07/16/memcached-003/
memcached全面剖析–2.理解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)。
圖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, 然後將資料緩衝於其中。
圖2 選擇儲存記錄的組的方法
實際上,Slab Allocator也是有利也有弊。下面介紹一下它的缺點。
Slab Allocator的缺點
Slab Allocator解決了當初的記憶體片段問題,但新的機制也給memcached帶來了新的問題。
這個問題就是,由於分配的是特定長度的記憶體,因此無法有效利用分配的記憶體。 例如,將100位元組的資料緩衝到128位元組的chunk中,剩餘的28位元組就浪費了(圖3)。
圖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.
就是說,如果預Crowdsourced Security Testing道用戶端發送的資料的公用大小,或者僅緩衝大小相同的資料的情況下, 只要使用適合資料大小的組的列表,就可以減少浪費。
但是很遺憾,現在還不能進行任何調優,只能期待以後的版本了。 但是,我們可以調節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 |
從這個指令碼獲得的資訊對於調優非常方便,強烈推薦使用。