memcached分散式原理與實現

行易難發表於2019-02-28

memcached分散式原理與實現

0x01 概況

1.1 什麼是memcached

memcached是一個分散式,開源的資料儲存引擎。

memcached是一款高效能的分散式記憶體快取伺服器,通過減少查詢次數來抵消沉重緩慢的資料集或API呼叫、提高應用響應速度、提高可擴充套件性。

在高併發的場景下, 大量的讀/寫請求湧向資料庫, 此時磁碟IO將成為瓶頸, 從而導致過高的響應延遲, 因此快取應運而生.

image

Memcached的工作方式是將關鍵詞和他們對應的值(最大能達到1MB)儲存在一個關聯矩陣中(比如雜湊表),延展和分佈在大量的虛擬伺服器中。

當然無論是單機快取還是分散式快取都有其適用場景和優缺點, 最常見的有redis和memcached. 本文主要是介紹memcached.

1.2 快取介紹 – 計算機體系快取

硬碟 –> 記憶體 –> 三級快取 –> 二級快取 –> 一級快取

image

1.3 快取介紹 – 快取應用系統

首先訪問較快的儲存介質, 如果命中且未生效則返回內容. 如果命中或失效則訪問較慢的儲存介質將內容返回同時更新快取.

image

1.4 memcached特點

特點 描述
協議簡單 它是基於文字行的協議,直接通過telnet在memcached伺服器上可進行存取資料操作
基於libevent事件處理 非同步I/O, 基於事件的單程式和單執行緒, 使用libevent作為事件處理機制;
內建記憶體儲存方式, 非永續性儲存 所有資料都儲存在記憶體中,存取資料比硬碟快,當記憶體滿後,通過LRU演算法自動刪除不使用的快取,但沒有考慮資料的容災問題,重啟服務,所有資料會丟失。
分散式 各個memcached伺服器之間互不通訊,各自獨立存取資料,不共享任何資訊。伺服器並不具有分散式功能,分散式部署取決於memcache客戶端。

0x02 安裝與使用

2.1 如何啟動-原始碼安裝

# 安裝依賴包
apt-get install libevent-dev

#安裝Memcached
wget http://memcached.org/latest
tar -zxvf memcached-1.x.x.tar.gz
cd memcached-1.x.x
./configure && make && make test && sudo make install

#啟動memcached
memcached -p 11211 -d -u root -P /tmp/memcached.pid
複製程式碼

-P是表示使用TCP,預設埠為11211
-d表示後臺啟動一個守護程式(daemon)
-u表示指定root使用者啟動,預設不能用root使用者啟動
-P表示程式的pid存放地點,此處“p”為大寫“P”
-l,後面跟IP地址,手工指定監聽IP地址,預設所有IP都在監聽
-m後面跟分配記憶體大小,以MB為單位,預設為64M
-c最大執行併發連線數,預設為1024
-f 塊大小增長因子,預設是1.25
-M 記憶體耗盡時返回錯誤,而不是刪除項,即不用LRU演算法 在64位系統中,會報libevent-1.4.so.2檔案無法找到,解決辦法是把32位目錄裡的同名檔案連結至64位目錄中,即像windows那樣建立快捷方式。 Shell > /usr/local/lib/libevent-1.4.so.2 /usr/lib64/libevent-1.4.so.2 啟動後如果發現沒有埠在監聽,是因為命動命令時帶pid引數的“p”是大寫“P”,你可能寫成小寫了。

2.2 如何啟動-docker安裝

// 執行一個記憶體限制為1024MB的容器:

sudo docker run -name csphere-memcached -m 1024m -d -p 11211:11211 memcached:alpine
複製程式碼

可以檢視Memcached:latest 使用的 Dockerfile, 來獲取 memcached在debian下的安裝方法

2.3 如何使用

分類 方法 描述
set 新增一個新條目到memcached或是用新的資料替換替換掉已存在的條目
add 當KEY不存在的情況下,它向memcached存資料,否則,返回NOT_STORED響應
replace 當KEY存在的情況下,它才會向memcached存資料,否則返回NOT_STORED響應
cas 改變一個存在的KEY值 ,但它還帶了檢查的功能
append 在這個值後面插入新值
prepend 在這個值前面插入新值
get 取單個值 ,從快取中返回資料時,將在第一行得到KEY的名字,flag的值和返回的value長度,真正的資料在第二行,最後返回END,如KEY不存在,第一行就直接返回END
get_multi 一次性取多個值
delete
# 清空memcache資料    

telnet 10.27.5.71 11211    
flush_all    
quit //退出telnet
複製程式碼

2.4 php使用memcached

yum -y install libmemcached.x86_64 libmemcached-devel.x86_64
 
cd /usr/src
git clone https://github.com/php-memcached-dev/php-memcached.git
cd php-memcached/
git checkout php7
/alidata/server/php/bin/phpize
./configure --with-php-config=/alidata/server/php/bin/php-config
make
make install
複製程式碼

2.5 管理與效能監控

可以通過命令列直接管理與監控也可通過nagios等監控軟體進行監控
命令列:

// 如果在啟動時指定了IP及埠號,這裡要作相應改動,連線成功後命令 
telnet 127.0.0.1 11211
複製程式碼
命令 描述
stats 統計memcached的各種資訊
stats reset 重新統計資料
stats slabs 顯示slabs資訊,可以詳細看到資料的分段儲存情況
stats items 顯示slab中的item數目
stats cachedump 1 0 列出slabs第一段裡存的KEY值
set/get 儲存/獲取資料
STAT evictions 0 表示要騰出新空間給新的item而移動的合法item數目

0x03 架構原理與分析

3.1 記憶體演算法

Memcached利用slab allocation機制來分配和管理記憶體,它按照預先規定的大小,將分配的記憶體分割成特定長度的記憶體塊,再把尺寸相同的記憶體塊分成組,資料在存放時,根據鍵值 大小去匹配slab大小,找就近的slab存放,所以存在空間浪費現象。

傳統的記憶體管理方式是,使用完通過malloc分配的記憶體後通過free來回收記憶體,這種方式容易產生記憶體碎片並降低作業系統對記憶體的管理效率

Memcached的記憶體管理制效率高,而且不會造成記憶體碎片,但是它最大的缺點就是會導致空間浪費。因為每個 Chunk都分配了特定長度的記憶體空間,所以變長資料無法充分利用這些空間。如圖二所示,將100個位元組的資料快取到128個位元組的Chunk中,剩餘的28個位元組就浪費掉了。

image

如何避免記憶體浪費

  • 預先計算出應用存入的資料大小,或把同一業務型別的資料存入一個Memcached伺服器中,確儲存入的資料大小相對均勻,這樣就可以減少對記憶體的浪費
  • 在啟動時指定“-f”引數,能在某種程度上控制記憶體組之間的大小差異
    • 在應用中使用Memcached時,通常可以不重新設定這個引數,使用預設值1.25進行部署
    • 如果想優化Memcached對記憶體的使用,可以考慮重新計算資料的預期平均長度,調整這個引數來獲得合適的設定值

3.2 刪除機制(快取策略)

Memcached的快取策略是**LRU(最近最少使用)**加上到期失效策略。當你在memcached記憶體儲資料項時,你有可能會指定它在快取的失效時間,預設為永久。當memcached伺服器用完分配的內時,失效的資料被首先替換,然後也是最近未使用的資料。在LRU中,memcached使用的是一種Lazy Expiration策略,自己不會監控存入的key/vlue對是否過期,而是在獲取key值時檢視記錄的時間戳,檢查key/value對空間是否過期,這樣可減輕伺服器的負載。

當空間佔滿時

  • 使用LRU演算法來分配空間,刪除最近最少使用的key/value對
  • 如果不使用LRU的話, 在啟動引數上加入”-M”, 記憶體耗盡時,會返回報錯資訊

3.3 分散式概述

memcached不相互通訊,那麼memcached是如何實現分散式的呢?

memcached的分散式實現主要依賴客戶端的實現.

當資料到達客戶端, 客戶端實現的演算法會根據”鍵”來決定儲存的memcached伺服器.
伺服器選定後, 命令他儲存資料.
取的時候也一樣, 客戶端根據”鍵”選擇伺服器, 使用儲存時候的相同演算法就能儲存選中和存的時候相同的伺服器.

也就是說,存取資料分二步走,第一步,選擇伺服器,第二步存取資料。

使用什麼演算法選擇伺服器呢?

client 使用Hash演算法來完成資料分散儲存.
複製程式碼

3.4 分散式演算法(Hash演算法)

當向memcached叢集存入/取出key/value時,memcached客戶端程式根據一定的演算法計算存入哪臺伺服器,然後再把key/value值存到此伺服器中。也就是說,存取資料分二步走,第一步,選擇伺服器,第二步存取資料。

常用的演算法有兩種: 餘數計算分散法 和 一致性Hash演算法.

3.4.1 餘數計算分散法

  • 標準的memcached分散式演算法
CRC($key)%N
複製程式碼

客戶端首先根據key來計算CPC, 然後結果對伺服器取模得到memcached伺服器節點

  • 被匹配到的伺服器無法連線時
    • rehash: 嘗試將連線的次數加到key後面, 重新hash
  • 新增/移除伺服器
    • 餘數計算分散發相當簡單,資料分散也很優秀
    • 幾乎所有的快取都會失效, 快取重組的代價相當的大。
  • 權重不易實現

3.4.2 一致性hash演算法

將server的hash值分配至0~2^32的圓環上, 用同樣的方法求出儲存數值鍵的hash值並對映到圓上. 然後從資料對映到的位置開始順時針查詢, 將資料存放至找到的第一臺伺服器上. 如果超過0~2^32還找不到, 則將資料存放至第一臺伺服器.

image

如果新添/刪除一臺server, 在一致性hash演算法下會有什麼影響?

image

如圖, 新添/刪除server時, 只在圓上增加伺服器的逆時針方向的第一臺伺服器上的鍵會受到影響。

優化一致性hash演算法

  • 一般的一致性hash演算法最大限度的抑制了鍵的重新分配, 並且有的實現方式還採用了虛擬節點的思想
  • 伺服器的對映地點的分佈非常的不均勻, 導致資料訪問傾斜, 大量的key被對映到同一臺伺服器上.
    • 虛擬節點: 每臺機器計算出多個hash值, 每個值對應環上的一個節點位置
    • key的對映方式不變, 就多了層從虛擬節點到再對映到物理機的過程

優化後: 在物理機很少的情況下, 只要虛擬節點足夠多, 也能使的key分佈相對均勻. 提升了演算法的平衡性.

image

3.5 Hash演算法的衡量指標

3.5.1 是單調性(Monotonicity)
單調性是指如果已經有一些內容通過雜湊分派到了相應的緩衝中,又有新的緩衝加入到系統中。雜湊的結果應能夠保證原有已分配的內容可以被對映到新的緩衝中去,而不會被對映到舊的緩衝集合中的其他緩衝區。
複製程式碼
3.5.2 平衡性
平衡性是指雜湊的結果能夠儘可能分佈到所有的緩衝中去,這樣可以使得所有的緩衝空間都得到利用。
複製程式碼

hash 演算法並不是保證絕對的平衡

0x04 常見問題

4.1 memcached與redis的區別?

對比點 memcached redis
資料結構 單一(儲存資料的型別都是String字串型別) 豐富(String、List、Set、Sortedset、Hash)
記憶體使用率 使用簡單的key-value儲存,Memcached的記憶體利用率更高 Redis採用hash結構來做key-value儲存,由於其組合式的壓縮,其記憶體利用率會高於Memcached
持久化 不可以 可以(可以把資料隨時儲存在磁碟上)
資料同步 不可以 可以
多核 可以使用多核(多執行緒) 單核
資料大小 單個key(變數)存放的資料有1M的限制 單個key(變數)存放的資料有1GB的限制
計算能力 本身有一定的計算功能
  • Redis在儲存小資料時比Memcached效能更高
  • 在100k以上的資料中,Memcached效能要高於Redis
  • memcached增加了網路IO的次數和資料體積

memcached和redis對過期資料的處理

Redis是懶處理,對有有效期的資料會做有效期的標識,在指定時間會對有過期時間的資料做處理。隨機取出一部分資料,檢查是否有過期資料,如果過期資料超過25/100,則反覆此過程

4.2 memcache為什麼快?

它使用libevent,可以應付任意數量開啟的連線(使用epoll,而非poll),使用非阻塞網路IO,分散式雜湊物件到不同的伺服器,查詢複雜度是O(1)。

4.3 Memcache快取命中率?

快取命中率 = get_hits/cmd_get * 100%

4.4 Memcache叢集實現

一致性Hash

0x05 總結

a. 在軟體工程的專案實戰中, 經常會遇到時間/空間的權衡, 快取是權衡中被研究出的一種典型的技術, 而memcached又是此類技術中的佼佼者.

b. 在高併發環境下,大量的讀、寫請求湧向資料庫,此時磁碟IO將成為瓶頸,從而導致過高的響應延遲,因此快取應運而生。

c. 本文在理解快取基本概念的情況下介紹了memcached的分散式演算法實現原理

相關文章