memcached

memcached是一個高性能的分佈式內存對象緩存系統:前端

Memcached簡介

Memcached 是一個高性能的分佈式內存對象緩存系統,用於動態Web應用以減輕數據庫負載。它經過在內存中緩存數據和對象來減小讀取數據庫的次數,從而提升動態、數據庫驅動網站的速度。Memcached基於一個存儲鍵/值對的hashmap。其守護進程(daemon )是用C寫的,可是客戶端能夠用任何語言來編寫,並經過memcached協議與守護進程通訊。node

爲何用Memcached

網站的高併發讀寫需求,傳統的數據庫開始出現瓶頸python

單機Mysql時代

在90年代,一個網站的訪問量通常都不大,用單個數據庫徹底能夠輕鬆應付。在那個時候,更多的都是靜態網頁,動態交互類型的網站很少。mysql

90年代的架構遇到的問題程序員

1.數據量的總大小 一個機器放不下時
2.數據的索引(B+ Tree)一個機器的內存放不下時
3.訪問量(讀寫混合)一個實例不能承受web

Memcached(緩存)+MySQL+垂直拆分面試

隨着訪問量的上升,幾乎大部分使用MySQL架構的網站在數據庫上都開始出現了性能問題,web程序再也不僅僅專一在功能上,同時也在追求性能。程序員們開始大量的使用緩存技術來緩解數據庫的壓力,優化數據庫的結構和索引。開始比較流行的是經過文件緩存來緩解數據庫壓力,可是當訪問量繼續增大的時候,多臺web機器經過文件緩存不能共享,大量的小文件緩存也帶了了比較高的IO壓力。在這個時候,Memcached出現了。算法

Memcached做爲一個獨立的分佈式的緩存服務器,爲多個web服務器提供了一個共享的高性能緩存服務,在Memcached服務器上,又發展了根據hash算法來進行多臺Memcached緩存服務的擴展,而後又出現了一致性hash來解決增長或減小緩存服務器致使從新hash帶來的大量緩存失效的弊端sql

Mysql主從讀寫分離數據庫

因爲數據庫的寫入壓力增長,Memcached只能緩解數據庫的讀取壓力。讀寫集中在一個數據庫上讓數據庫不堪重負,大部分網站開始使用主從複製技術來達到讀寫分離,以提升讀寫性能和讀庫的可擴展性。Mysql的master-slave模式成爲這個時候的網站標配了。

分表分庫+水平拆分+mysql集羣

在Memcached的高速緩存,MySQL的主從複製,讀寫分離的基礎之上,這時MySQL主庫的寫壓力開始出現瓶頸,而數據量的持續猛增,因爲MyISAM使用表鎖,在高併發下會出現嚴重的鎖問題,大量的高併發MySQL應用開始使用InnoDB引擎代替MyISAM。

同時,開始流行使用分表分庫來緩解寫壓力和數據增加的擴展問題。這個時候,分表分庫成了一個熱門技術,是面試的熱門問題也是業界討論的熱門技術問題。也就在這個時候,MySQL推出了還不太穩定的表分區,這也給技術實力通常的公司帶來了但願。雖然MySQL推出了MySQL Cluster集羣,但性能也不能很好知足互聯網的要求,只是在高可靠性上提供了很是大的保證。

Mysql的拓展性能

MySQL數據庫也常常存儲一些大文本字段,致使數據庫表很是的大,在作數據庫恢復的時候就致使很是的慢,不容易快速恢復數據庫。好比1000萬4KB大小的文本就接近40GB的大小,若是能把這些數據從MySQL省去,MySQL將變得很是的小。關係數據庫很強大,可是它並不能很好的應付全部的應用場景。MySQL的擴展性差(須要複雜的技術來實現),大數據下IO壓力大,表結構更改困難,正是當前使用MySQL的開發人員面臨的問題。

今天的架構

解答

多數的Web數據應用都保存到了關係型數據庫中,如Mysq,Web服務器從中讀取數據並在瀏覽器中顯示.可是隨着數據量的增大,訪問的集中,關係型數據庫性能出現瓶頸,響應速度慢致使網站打開延遲等問題,因此Memcached的主要的目的是經過自身內存中緩存關係型數據庫查詢的查詢結果,減小數據庫自身被訪問的次數,以提升動態Web應用速度,提升網站架構的併發能力和拓展屬性

經過事先規劃好的系統內存空間中臨時緩存數據庫中的各類數據,已達到減小前端業務服務對關係型數據庫的直接高併發訪問,從而達到提高大規模數據集羣中動態服務的併發訪問能力.

Web服務器讀取數據時先讀Memcached服務器,若是Memcached沒有.則向數據庫請求數據.而後Web再報請求到的數據發送到Memcached.

Memcached 特徵

協議簡單

所以,經過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 (數據)

 事件處理

libevent是個程序庫,它將Linux的epoll、BSD類操做系統的kqueue等事件處理功能封裝成統一的接口。即便對服務器的鏈接數增長,也能發揮O(1)的性能。memcached使用這個libevent庫,
所以能在Linux、BSD、Solaris等操做系統上發揮其高性能。關於事件處理,能夠參考Dan Kegel的The C10K Problem。

 內存存儲方式

爲了提升性能,memcached中保存的數據都存儲在memcached內置的內存存儲空間中。因爲數據僅存在於內存中,所以重啓memcached、重啓操做系統會致使所有數據消失。
另外,內容容量達到指定值以後,就基於LRU(Least Recently Used)算法自動刪除不使用的緩存。memcached自己是爲緩存而設計的服務器,所以並無過多考慮數據的永久性問題。

 Memcached 不互通訊的分佈式

memcached儘管是「分佈式」緩存服務器,但服務器端並無分佈式功能。各個memcached不會互相通訊以共享信息。那麼,怎樣進行分佈式呢?這徹底取決於客戶端的實現。本文也將介紹memcached的分佈式。

 

memcached的分佈式

memcached的內存存儲

最近的memcached默認的狀況下采用了名爲Slab Allocation的機制分配,在該機制出現以前,內存的分配經過對全部記錄進行簡單的malloc和free來進行的.可是這種方式會致使內存碎片,家中操做系統內存管理的負擔,在最壞的狀況下會致使操做系統memcached進程,Slab Allocation就是爲了解決這個問題的

原理

將分配的內存分割成各類尺寸的塊(chunk), 並把尺寸相同的塊分紅組(chunk的集合),每一個chunk集合被稱爲slab。

Slab Allocation能夠重複使用已分配的內存的目的.分配到的內存不會被釋放,而是被重複利用.

主要術語

Page

分配給Slab的內存空間,默認是1MB.會分配給Slab以後跟庫slab的大小切分紅chunk.

Chunk

用於緩存記錄的內存空間

Slab Class

特定大小的chunk的組

選擇存儲記錄的組的方法

缺點

因爲分配的時特定長度的內存,所以沒法又想利用分配的內存.好比將100字節的數據緩存到128字節的chunk中,剩餘的28字節就浪費掉了

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

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」策略。

能夠看到從從1字節開始組的大小增大爲原來的2倍,這樣設置的問題是slab之間的差異比較大,有些狀況下就比較浪費內存,所以,爲了減小內存浪費,兩年前追加了growth facthor這個選項

從圖中可見,組間的差距比2時小的多,更合適緩存幾百字節的記錄,從上面的輸出結果來看,可能會有些計算偏差.這些偏差是爲了保持字節數的對齊故意設置的.

將memcached引入產品,或是直接使用默認值進行部署時,最好是從新計算一下數據的預期平均長度,調整growth factor以得到恰當的長度,避免內存的浪費.

查看memcached的內部狀態

首先開啓memcached服務

其次鏈接服務

詳細的信息能夠參考memcached軟件包內的protocol.txt

查看slabs的使用情況

參數的含義

含義
# slab class編號
Item_Size Chunk大小
Max_age LRU內最舊的記錄生存時間
Pages 分配給Slab的頁數
Count Slab內的記錄數
Full? Slab內是否含有空閒chunk
Evicted 從LRU中移除未過時item的次數
Evict_Time 最後被移除緩存的時間,0表示當前就有被移除
OOM M參數

memcached的分佈式

下面假設memcached有三臺服務器node1-node3,保存的鍵名是」tokyo」,「kanagawa」,「chiba」,「saitma」,「gunma」

首先向memcached中添加「tokyo」,將「tokyo」傳給客戶端程序庫後,客戶端實現的算法就會根據」鍵」來決定保存數據的memcached服務器,服務器選定以後,命令他保存」totyo」及其值.一樣的」kanagawa」,「chiba」,「saitma」,「gunma」都是先選擇服務器再保存的.

接下來獲取保存的數據,獲取時也要獲取的鍵」tokyo」傳遞給函數庫.函數庫經過與數據庫保存相同的算法,根據」鍵」選擇服務器.使用的算法相同,就能選中與保存相同的服務器,而後發送get命令.只要數據沒有由於某些緣由被刪除,就能得到保存的值.

這樣不一樣的鍵保存到不一樣的不一樣的不一樣的服務器上,就實現memcached的分佈式,memcached服務器增多後,鍵就會分散,即便一臺memcached服務器發生故障沒法連接,也不會影響其餘的緩存,系統依舊可以繼續運行.

算法實現

​ 首先求的字符串的CRC根據該值處於服務器節點數目獲得的餘數決定服務器,當選擇的服務器沒法鏈接的時候,Cache:Memcached將會連接次數添加到鍵以後,再次計算哈希值並嘗試連接.這個動做叫作rehash.

use strict;
use warnings;
use String::CRC32;
my @nodes = @ARGV;
my @keys = (’a’..’z');
my %nodes;
foreach my $key ( @keys ) {
my $hash = crc32($key);
my $mod = $hash % ( $#nodes + 1 );
my $server = $nodes[ $mod ];
push @{ $nodes{ $server } }, $key;
}
foreach my $node ( sort keys %nodes ) {
printf 「%s: %s\n」, $node, join 「,」, @{ $nodes{$node} };

 

執行結果
tokyo       => node2
kanagawa => node3
chiba       => node2
saitama   =>node1
gunma     =>node1

 

求餘算法缺點

添加或移除服務器時,緩存重組的代價至關巨大。 添加服務器後,餘數就會產生鉅變,這樣就沒法獲取與保存時相同的服務器, 從而影響緩存的命中率。

Consistent Hashing算法

1)  首先求出memcached服務器(節點)的哈希值,並將其配置到0~232的圓(continuum)上。

 2)  而後用一樣的方法求出存儲數據的鍵的哈希值,並映射到圓上。 

 3)  而後從數據映射到的位置開始順時針查找,將數據保存到找到的第一個服務器上。若是超過232仍然找不到服務器,就會保存到第一臺memcached服務器上。

 

Consistent Hashing:添加服務器

相關文章
相關標籤/搜索