1. 程式人生 > >Linux之搭建memcache緩存服務器

Linux之搭建memcache緩存服務器

duyuheng linux memcache緩存服務器

Linux之搭建memcache緩存服務器(一)

一、MemCache

session

MemCache是一個自由、源碼開放、高性能、分布式的分布式內存對象緩存系統,用於動態Web應用以減輕數據庫的負載。它通過在內存中緩存數據和對象來減少讀取數據庫的次數,從而提高了網站訪問的速度。 MemCaChe是一個存儲鍵值對的HashMap,在內存中對任意的數據(比如字符串、對象等)所使用的key-value存儲,數據可以來自數據庫調用、API調用,或者頁面渲染的結果。MemCache設計理念就是小而強大,它簡單的設計促進了快速部署、易於開發並解決面對大規模的數據緩存的許多難題,而所開放的API使得MemCache

能用於JavaC/C++/C#PerlPythonPHPRuby等大部分流行的程序語言。

另外,說一下為什麽會有Memcachememcached兩種名稱?其實Memcache是這個項目的名稱,而memcached是它服務器端的主程序文件名

MemCache的官方網站為 http://memcached.org/

MemCache訪問模型

為了加深memcache理解,以memcache為代表的分布式緩存,訪問模型如下:

技術分享

特別澄清一個問題,MemCache雖然被稱為分布式緩存,但是MemCache本身完全不具備分布式的功能,MemCache集群之間不會相互通信(與之形成對比的,比如JBoss Cache,某臺服務器有緩存數據更新時,會通知集群中其他機器更新緩存或清除緩存數據),所謂的分布式,完全依賴於客戶端程序的實現,就像上面這張圖的流程一樣。

同時基於這張圖,理一下MemCache一次寫緩存的流程:

1、應用程序輸入需要寫緩存的數據

2APIKey輸入路由算法模塊,路由算法根據KeyMemCache集群服務器列表得到一臺服務器編號

3、由服務器編號得到MemCache及其的ip地址和端口號

4API調用通信模塊和指定編號的服務器通信,將數據寫入該服務器,完成一次分布式緩存的寫操作

讀緩存和寫緩存一樣,只要使用相同的路由算法和服務器列表,只要應用程序查詢的是相同的KeyMemCache客戶端總是訪問相同的客戶端去讀取數據,只要服務器中還緩存著該數據,就能保證緩存命中。

這種MemCache集群的方式也是從分區容錯性的方面考慮的,假如Node2宕機了,那麽Node2上面存儲的數據都不可用了,此時由於集群中Node0Node1還存在,下一次請求Node2中存儲的Key值的時候,肯定是沒有命中的,這時先從數據庫中拿到要緩存的數據,然後路由算法模塊根據Key值在Node0Node1中選取一個節點,把對應的數據放進去,這樣下一次就又可以走緩存了,這種集群的做法很好,但是缺點是成本比較大。

一致性Hash算法

從上面的圖中,可以看出一個很重要的問題,就是對服務器集群的管理,路由算法至關重要,就和負載均衡算法一樣,路由算法決定著究竟該訪問集群中的哪臺服務器,先看一個簡單的路由算法。

1、余數Hash

簡單的路由算法可以使用余數Hash:用服務器數目和緩存數據KEYhash值相除,余數為服務器列表下標編號,假如某個str對應的HashCode52、服務器的數目是3,取余數得到1str對應節點Node1,所以路由算法把str路由到Node1服務器上。由於HashCode隨機性比較強,所以使用余數Hash路由算法就可以保證緩存數據在整個MemCache服務器集群中有比較均衡的分布。

如果不考慮服務器集群的伸縮性,那麽余數Hash算法幾乎可以滿足絕大多數的緩存路由需求,但是當分布式緩存集群需要擴容的時候,就難辦了。

就假設MemCache服務器集群由3臺變為4臺吧,更改服務器列表,仍然使用余數Hash524的余數是0,對應Node0,但是str原來是存在Node1上的,這就導致了緩存沒有命中。舉個例子,原來有HashCode0~1920個數據,那麽:

那麽不妨舉個例子,原來有HashCode0~1920個數據,那麽:

技術分享

現在擴容到4臺,加粗標紅的表示命中:

技術分享

如果擴容到20+的臺數,只有前三個HashCode對應的Key是命中的,也就是15%。當然現實情況肯定比這個復雜得多,不過足以說明,使用余數Hash的路由算法,在擴容的時候會造成大量的數據無法正確命中(其實不僅僅是無法命中,那些大量的無法命中的數據還在原緩存中在被移除前占據著內存)。在網站業務中,大部分的業務數據度操作請求上事實上是通過緩存獲取的,只有少量讀操作會訪問數據庫,因此數據庫的負載能力是以有緩存為前提而設計的。當大部分被緩存了的數據因為服務器擴容而不能正確讀取時,這些數據訪問的壓力就落在了數據庫的身上,這將大大超過數據庫的負載能力,嚴重的可能會導致數據庫宕機。

這個問題有解決方案,解決步驟為:

1)在網站訪問量低谷,通常是深夜,技術團隊加班,擴容、重啟服務器

2)通過模擬請求的方式逐漸預熱緩存,使緩存服務器中的數據重新分布

2、一致性Hash算法

一致性Hash算法通過一個叫做一致性Hash環的數據結構實現Key到緩存服務器的Hash映射。簡單地說,一致性哈希將整個哈希值空間組織成一個虛擬的圓環(這個環被稱為一致性Hash環),如假設某空間哈希函數H的值空間是0~2^32-1(即哈希值是一個32位無符號整形),整個哈希空間如下

技術分享

下一步將各個服務器使用H進行一個哈希計算,具體可以使用服務器的IP地址或者主機名作為關鍵字,這樣每臺機器能確定其在上面的哈希環上的位置了,並且是按照順時針排列,這裏我們假設三臺節點memcache經計算後位置如下

技術分享

接下來使用相同算法計算出數據的哈希值h,並由此確定數據在此哈希環上的位置

假如我們有數據ABCD4個對象,經過哈希計算後位置如下:

技術分享

根據一致性哈希算法,數據A就被綁定到了server01上,D被綁定到了server02上,BCserver03上,是按照順時針找最近服務節點方法

這樣得到的哈希環調度方法,有很高的容錯性和可擴展性:

假設server03宕機

技術分享

可以看到此時CB會受到影響,將BC被重定位到Server01。一般的,在一致性哈希算法中,如果一臺服務器不可用,則受影響的數據僅僅是此服務器到其環空間中前一臺服務器(即順著逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。

考慮另外一種情況,如果我們在系統中增加一臺服務器Memcached Server 04

技術分享

此時ADC不受影響,只有B需要重定位到新的Server04。一般的,在一致性哈希算法中,如果增加一臺服務器,則受影響的數據僅僅是新服務器到其環空間中前一臺服務器(即順著逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。

綜上所述,一致性哈希算法對於節點的增減都只需重定位環空間中的一小部分數據,具有較好的容錯性和可擴展性。

一致性哈希的缺點:在服務節點太少時,容易因為節點分部不均勻而造成數據傾斜問題。我們可以采用增加虛擬節點的方式解決。

更重要的是,集群中緩存服務器節點越多,增加/減少節點帶來的影響越小,很好理解。換句話說,隨著集群規模的增大,繼續命中原有緩存數據的概率會越來越大,雖然仍然有小部分數據緩存在服務器中不能被讀到,但是這個比例足夠小,即使訪問數據庫,也不會對數據庫造成致命的負載壓力。

MemCache實現原理

首先要說明一點,MemCache的數據存放在內存中

1、訪問數據的速度比傳統的關系型數據庫要快,因為OracleMySQL這些傳統的關系型數據庫為了保持數據的持久性,數據存放在硬盤中,IO操作速度慢

2MemCache的數據存放在內存中同時意味著只要MemCache重啟了,數據就會消失

3、既然MemCache的數據存放在內存中,那麽勢必受到機器位數的限制,32位機器最多只能使用2GB的內存空間,64位機器可以認為沒有上限

然後我們來看一下MemCache的原理,MemCache最重要的是內存如何分配的,MemCache采用的內存分配方式是固定空間分配,如下圖所示

技術分享

這張圖片裏面涉及了slab_classslabpagechunk四個概念,它們之間的關系是:

1MemCache將內存空間分為一組slab

2、每個slab下又有若幹個page,每個page默認是1M,如果一個slab占用100M內存的話,那麽這個slab下應該有100page

3、每個page裏面包含一組chunkchunk是真正存放數據的地方,同一個slab裏面的chunk的大小是固定的

4、有相同大小chunkslab被組織在一起,稱為slab_class

MemCache內存分配的方式稱為allocator(分配運算)slab的數量是有限的,幾個、十幾個或者幾十個,這個和啟動參數的配置相關。

MemCache中的value存放的地方是由value的大小決定的,value總是會被存放到與chunk大小最接近的一個slab中,比如slab[1]chunk大小為80字節、slab[2]chunk大小為100字節、slab[3]chunk大小為125字節(相鄰slab內的chunk基本以1.25為比例進行增長,MemCache啟動時可以用-f指定這個比例),那麽過來一個88字節的value,這個value將被放到2slab中。放slab的時候,首先slab要申請內存,申請內存是以page為單位的,所以在放入第一個數據的時候,無論大小為多少,都會有1M大小的page被分配給該slab。申請到page後,slab會將這個page的內存按chunk的大小進行切分,這樣就變成了一個chunk數組,最後從這個chunk數組中選擇一個用於存儲數據。

如果這個slab中沒有chunk可以分配了怎麽辦,如果MemCache啟動沒有追加-M(禁止LRU,這種情況下內存不夠會報Out Of Memory錯誤),那麽MemCache會把這個slab中最近最少使用的chunk中的數據清理掉,然後放上最新的數據。

Memcache的工作流程:

技術分享

1、檢查客戶端的請求數據是否在memcached中,如果有,直接把請求數據返回,不再對數據庫進行任何操作,路徑操作為①②③⑦。
2、如果請求的數據不在memcached中,就去查數據庫,把從數據庫中獲取的數據返回給客戶端,同時把數據緩存一份到memcached中(memcached客戶端不負責,需要程序明確實現),路徑操作為①②④⑤⑦⑥。
3、每次更新數據庫的同時更新memcached中的數據,保證一致性。
4、當分配給memcached內存空間用完之後,會使用LRULeast Recently Used,最近最少使用)策略加上到期失效策略,失效數據首先被替換,然後再替換掉最近未使用的數據。

Memcached特征:
協議簡單:
它是基於文本行的協議,直接通過telnetmemcached服務器上可進行存取數據操作

註:文本行的協議:指的是信息以文本傳送,一個信息單元傳遞完畢後要傳送換行。比如對於HTTPGET請求來說,GET /index.html HTTP/1.1是一行,接下去每個頭部信息各占一行。一個空行表示整個請求結束

基於libevent事件處理:
Libevent是一套利用C開發的程序庫,它將BSD系統的kqueue,Linux系統的epoll等事件處理功能封裝成一個接口,與傳統的select相比,提高了性能。
內置的內存管理方式:
所有數據都保存在內存中,存取數據比硬盤快,當內存滿後,通過LRU算法自動刪除不使用的緩存,但沒有考慮數據的容災問題,重啟服務,所有數據會丟失。
分布式
各個memcached服務器之間互不通信,各自獨立存取數據,不共享任何信息。服務器並不具有分布式功能,分布式部署取決於memcache客戶端。

Memcache的安裝

分為兩個過程:memcache服務器端的安裝和memcached客戶端的安裝。

所謂服務器端的安裝就是在服務器(一般都是linux系統)上安裝Memcache實現數據的存儲。

所謂客戶端的安裝就是指php(或者其他程序,Memcache還有其他不錯的api接口提供)去使用服務器端的Memcache提供的數據,需要php添加擴展。

PHPMemcache


本文出自 “duyuheng” 博客,謝絕轉載!

Linux之搭建memcache緩存服務器