經典分布式論文閱讀:Memcache

2019-09-18     IT技術分享

本文是Memcached論文的閱讀筆記,本文主要描述了如何使用 memcached 構建一個分布式鍵值存儲系統。

一個社交網絡的基礎設施應該滿足以下特性:

  1. 允許幾乎實時的通訊;
  2. 從多個源頭快速聚合內容;
  3. 能夠訪問和更新熱門的共享內容;
  4. 每秒能夠處理百萬用戶請求。

本文以 memcached 為基礎構建分布式鍵值存儲系統,在特定的規模之下,某些特性需要更多的努力去滿足。

總覽

以下兩個特性影響了系統的設計:

  1. 用戶消費的內容比生產的內容多得多;
  2. 一個讀取操作需要從多個源頭獲取數據。=-

下文使用 memcached 指代原始碼或者運行中二進位文件,使用 memcached 指代分布式系統。

查詢緩存: memcache 作為demand-filled look-aside緩存來使用,當某個Web請求數據的時候,首先嘗試從緩存中獲取,如果緩存中不存在,那麼從後端獲取數據並寫回緩存,如果更新數據的話,需要將舊的數據從緩存中刪除。

通用緩存: memcache 也可以作為通用的鍵值存儲來使用,例如保存機器學習計算結果等等。

本文描述了不同尺度下的重點,但是始終強調兩個設計目標:

  1. 任何改進必須解決用戶或者運維問題;
  2. 將讀取到短暫的過時數據的可能性作為可以調整的參數。

集群尺度:延遲和負載

當 memcache 運行在集群尺度上的時候,主要考慮的問題是減少解析緩存數據延遲和緩存缺失帶來的負載。

降低延遲

memcache 使用一致哈希將存儲對象分散到各個伺服器上,因此全部Web伺服器短時間內會連接所有的伺服器。降低延遲的工作主要在客戶端完成序列化、壓縮、請求路由、錯誤處理和批量請求等工作

並行請求和批量:可以使用有向無環圖分析數據之間的依賴關係,使得一次請求獲取儘可能多的鍵值。

客戶端-伺服器通訊:伺服器之間不進行互相通信,由客戶端處理系統複雜性。客戶端的邏輯由兩部分組成:

mcrouter

get 請求使用UDP協議來降低延遲和負載,如果數據包丟失或者亂序將被視為錯誤。Web伺服器將客戶端錯誤視為緩存缺失,但是從數據源獲取數據不做寫回。 set 和 delete 操作通過TCP協議進行。每個線程創建一個連接非常浪費資源,運行在同一個機器上 mcrouter 將這些連接進行合併,提高網絡利用率。

請求擁塞: memcache 使用流控制機制來限制請求擁塞,客戶端使用滑動窗口來控制請求數量,窗口隨著成功請求數據而擴大,隨著未答覆請求而減小。

過小的窗口會導致不必要的等待,而過大的窗口會導致請求阻塞,需要找到平衡點。

降低負載

租期:系統使用了租期機制來解決 過時數據驚群效應 。並發更新亂序之後會產生過時的數據。頻繁的寫入操作會頻繁地使得緩存失效,大量的讀取操作會同時轉向請求數據源。

當客戶端請求的緩存缺失的時候,伺服器分配一個和鍵綁定的租期,在寫回數據的時候需要驗證租期合法性。如果驗證之前伺服器收到過刪除請求,那麼租期就失效了。

驚群效應可以限制租期產生速率解決,例如10秒鐘一個,如果其他客戶端在這個10秒之前請求對應的鍵,那麼讓它等待一小會兒時間,當客戶端再次嘗試的時候,數據一般已經在緩存之中。

某些場景下過時數據是可以接受的,當數據被刪除後被轉移到一個保存最近刪除數據的數據結構中,存活一小段時間,一個獲取請求返回租期或者過時的數據。

Memcache池:不同的應用程式使用同一個 memcache 會相互干涉產生負作用,因此可以考慮將 memcached 伺服器分散到不同的池中,將不同讀寫特性的數據存入不同的池中。

在池中多副本:如果滿足以下特性,那麼可以在池中建立多副本:

memcached

如果使用了多副本,那麼在使緩存失效時要對所有副本進行。

故障處理

我們必須解決兩種規模下的故障:

  1. 因為網絡或者伺服器錯誤導致少量主機不可達;
  2. 影響集群中大部分伺服器的大規模宕機。

對於小規模的宕機,系統設置了少量稱為Gutter的機器來暫時管理不可用機器的鍵範圍,Gutter中的數據有效期很短,這樣避免了需要使緩存失效的操作。

區域尺度:副本

隨著負載規模增大,簡單地擴充機器是不夠的。作者將Web伺服器和 memcached 伺服器分為多個前端集群,和一個存儲集群組成一個 區域

區域失效

存儲集群保存著最新數據,用戶請求將數據副本寫入到前端伺服器。存儲集群負責發送緩存失效命令,來保持數據的一致性。對數據產生修改的SQL會附上需要失效的 mamcache 鍵,應用更改之後由 mcsqueal 將緩存失效消息發送給各個前端集群中的伺服器。

降低數據包率:如果直接將失效緩存消息會浪費網絡資源,因此需要將緩存失效消息批量發送給每個前端集群中的 mcrouter ,再由 mcrouter 轉發給集群內的伺服器。

通過Web伺服器失效:之所以不讓Web伺服器直接發送緩存失效消息是因為:

  • 通過Web伺服器進行批量處理比較低效
  • 在消息路由錯誤時候方便補救

區域池

集群獨立地緩存數據,一個數據可能在多個集群上有副本,可以通過讓兩個集群共享 memcached 伺服器,這種形式稱為區域池。可以根據訪問速率、數據集大小和訪問用戶數量來決定是否將數據存入區域池。

集群冷啟動

如果上線一個新的集群,那麼剛開始負載都會指向資料庫。作者通過 冷啟動預熱 機制解決,允許Web伺服器從其他集群獲取數據並寫回。

當然這樣會帶來不一致的問題,例如當一個客戶端更新了數據,同時另外一個客戶端剛好轉向其他集群獲取數據,剛好緩存失效消息還沒有到達。作者採用刪除拖延解決這個問題,在收到刪除消息兩秒之內禁止寫回。當客戶端寫回被拒絕後,說明數據已經被更新,需要從資料庫獲取。

跨區域:一致性

在多個地理位置部署數據中心有以下好處:

  1. 將Web伺服器放在距離用戶近的位置可以減少延遲
  2. 地理分散可以緩解自然災害和斷電事故
  3. 新的地點可能會提供更廉價的電力以及其他經濟效益

跨區域方式設計成一個區域管理主資料庫,其他區域保存只讀副本。這樣帶來的一個問題就是:副本數據和主資料庫之間存在一些延遲。系統在強調性能和可用的同時,提供盡最大力的最終一致性。

寫入主區:使用 mcsqueal 傳輸失效消息可以避免失效信息先於數據更新到達。

寫入非主區:如果一個非主區域的用戶更新了數據,那麼如果它接著從本地區域讀取數據可能看不到更改。這種情況下要求必須在本地資料庫趕上之後才能重來填充緩存,作者使用了 遠程標記 來解決這個問題。

當Web伺服器更新鍵值k時:

  1. 在區域建立一個遠程標記
  2. 在主區域進行寫操作,並指示使k和 失效
  3. 刪除本地集群上的k

這樣當k缺失的時候,如果客戶端看到 ,那麼就去從主區域獲取數據。

運維考慮:跨區域通信代價高,因此刪除消息和副本同步共用一個信道。

單機優化

性能優化: memcached 主要的性能優化為:

  1. 允許哈希表自動展開,避免查詢時間退化到
  2. 讓多線程伺服器使用全局鎖來保護多種數據結構
  3. 分配給線程獨立的UDP埠來避免數據傳輸競爭

自適應的slab分配器:

memcached 使用slab分配器管理內存,將內存空間塊組織成slab類,為數據分配內存的時候會尋找符合其大小最小的塊,如果內存塊不足則繼續申請。當機器內存不足以分配新的內存塊是,淘汰類中的LRU內存塊。

作者優化了slab分配器來調整slab中內存塊數量來適應負載。如果某個類正在淘汰內存塊並且下一個要被淘汰的數據比每一類平均要淘汰的數據新20%,那麼說明這個類需要更多的內存塊。從當前保存全局LRU的slab中回收內存分配給這個slab。

短周期數據緩存:短時間活動產生的短命鍵會浪費內存,作者使用混合策略來解決這個問題:對大多數鍵進行惰性回收,對短命鍵進行主動回收。把短命鍵數據放在一個按照過期時間排序的鍊表裡面,每秒鐘回收頭部的短命鍵。

軟體更新:為了在軟體更新過程儘可能減少中斷時間,作者修改了 memcache ,將緩存和數據結構保存在System V共享內存區域中,避免在更新時丟失數據。

end:如果你覺得本文對你有幫助的話,記得關注點贊轉發,你的支持就是我更新動力。

文章來源: https://twgreatdaily.com/zh-hk/gMUnQ20BJleJMoPM6r12.html