Facebook 對 Memcache 伸縮性的增強

類別: IT

概要:Memcached 是一個知名的,簡單的,全記憶體的快取方案。這篇文章描述了facebook是如何使用memcached來構建和擴充套件一個分散式的key-value儲存來為世界上最大的社交網站服務的。我們的系統每秒要處理幾十億的請求,同時儲存了幾萬億的資料項,可以給全世界超過10億的使用者提供豐富體驗。

1 介紹

近些年SNS網路大行其道,這對網站基礎建設提出了巨大的挑戰。每天有億萬的使用者在使用這些網路服務,巨大的計算、網路和I/O資源的需求使傳統的web架構不堪重 負。SNS網站的基礎架構需要滿足:1、近乎實時的交流;2、即時聚合不同來源的內容;3、訪問和更新非常熱門的共享內容;4、每秒處理幾百萬的使用者請求。

我們將描述我們是如何改進memcached[14]的開源版本,並且用它作為元件來構建用於世界上最大的社會化網路的分散式key-value儲存的。我們會討論從單叢集伺服器擴充套件成地理上分散式的多叢集的歷程。據我們所知,這個系統是世界上已安裝的規模最大的memcached系統,每秒可以處理幾十億的請求,儲存數以萬億的資料項。

本文是關於認識分散式key-value儲存的靈活性和實用性的系列文章[1, 2, 5, 6, 12, 14, 34, 36]的最後一篇。本文關注於memcached,這是一個全記憶體雜湊表的開源實現,它以較低的開銷提供了對共享儲存的低遲延訪問。有了這些特性我們可以構建資料密集的功能,否則是不可能的。例如,如果一個頁面請求會產生數以百計的資料庫請求,那麼這樣的功能只能停止在原型階段,因為實現起來會太慢,代價也太高。然而,在我們的應用裡,web頁面通常都會從memcached伺服器獲取數以千計的key-value對。

我們的目標之一,是展現部署在不同尺度(系統)上的重要主題。雖然在所有尺度上是很重要的品質,如效能,效率,容錯性和一致性,我們的經驗表明,在特定大小的一些素質要求比別人更多的努力來實現。舉例來說,保持資料的一致性,如果複製的內容是小量的,可以更容易在小尺度的網路上實現,相比較大的網路往往只是複製必要的內容。此外,找到一個最佳的通訊排程的重要性增加的數量增加伺服器和網路工作成為瓶頸。

本文包括四個主要貢獻:(1)我們描述了Facebook的基於memcach架構的演化。 (2)我們確定memcached的提高效能和增加記憶體效率的改進。 (3)我們簡明扼要地講述提高我們的經營能力我們的系統規模的機制。 (4)我們對生產工作負載賦予了特色(譯者加:對工作負載進行了分類?)。

2綜述

以下特點大大影響了我們的設計。第一,使用者閱讀的內容比他們建立的要多一個數量級,這種行為(讀寫的特點)所產生工作負載,顯然讓快取可以發揮很大的優勢。第二,我們是從多個來源讀取資料的,比如MySQL資料庫、HDFS裝置和後臺服務,這種多樣性要求一個靈活的快取策略,能夠從各個獨立的源中儲存資料。

MemCached提供了一組簡單的操作(set、get和delete),使它在一個大規模的分散式系統中成為注目的基礎元件。開源版本提供了單機記憶體雜湊表,在本文中,我們從這個開源版本開始,討論我們是怎麼使用這個基礎元件,使它變得更有效,並用它來建一個可以處理每秒數十億請求的分散式的鍵-值儲存系統。接下來,我們用“memcached”來指代它的原始碼或者它執行的二進位制例項,用“memcache”來指代由每個例項構成的分散式系統。


圖1:Memcache作為填補需求的旁路快取系統。左半圖說明了WEB伺服器讀取快取時命中失敗的讀取路徑,右半圖說明其寫路徑。

查詢快取:我們依賴於memcache來減輕讀取資料庫的負擔。特別的,我們使用memcache作為填補需求的旁路快取系統,如圖1。當一個Web伺服器需要資料時,首先通過一個字串的鍵在memcache中請求,如果沒有找到,它會從資料庫或者從後臺服務中檢索,再使用該鍵把結果存回memcache中。對於寫的請求,Web伺服器傳送SQL語句到資料庫,接著傳送刪除請求到memcache,使舊的快取資料失效。因為刪除是冪等運算,所以我們使用刪除快取的方式,而不是更新快取。

在應對MySQL資料庫繁重的查詢通訊的眾多方法中,我們選擇了memcache,在有限的資源與時間限制下,這是最好的選擇。此外,快取層與持久層分離,讓我們可以在工作負載發生變化時快速地調整。

通用快取:我們同樣讓memcache成為一個更加通用的鍵-值儲存系統。比如說,工程師們使用memcache儲存複雜的機器學習演算法的中間結果,這些結果能被很多其它應用程式所使用。它只需要我們付出很少的努力,就可以讓新增的服務利用現有的正在使用的基礎設施,而無需調整、優化、調配和維護大型的伺服器群。

正如memcached沒有提供伺服器到伺服器的協同,它僅僅是執行在單機上的一個記憶體雜湊表。接下來我們描述我們是如何基於memcached構建一個分散式鍵值儲存系統,以勝任在Facebook的工作負載下的操作。

圖2:整體架構

論文的結構主要描述了在三種不同的規模下出現的問題。當我們擁有第一個伺服器叢集時,頻繁的讀負載和廣泛的輸出是我們最大的擔心。當有必要擴充套件到多個前端叢集時,我們解決了叢集間的資料備份問題。最後,我們描述了一種機制,這種機制讓我們可以在全世界伸展叢集的同時提供平滑的使用者體驗。不論在什麼尺度上,容錯性和操作複雜性總是很重要的。我們展示了重要的資料參考,這些資料指引我們做出了最終的設計決定,讀者如需獲得更多細節性的分析,請參看Atikoglu et al.[8]的工作。提綱挈領的解釋參看圖2,這是最終的架構,我們將並置叢集組織起來,形成一個群體(region),指定一個主群體(master),由主群體提供資料流讓非主群體保持資料同步。

在系統的發展中,我們將這兩個重大的設計目標放在首位:

1. 只有已經對使用者或者我們的運維產生影響的問題,才值得改變。我們極少考慮範圍有限的優化。

2. 對陳舊資料的瞬態讀取,其概率和響應度類似,都將作為引數來調整。我們會暴露輕度陳舊的資料以便後臺儲存和高強度負載絕緣。

3 叢集之中: 延遲和負載

現在考慮叢集中數以千計的伺服器所帶來的挑戰。在這種規模之下,我們著眼於減少獲取快取時的負載,以及快取不中時資料庫的負載。

3.1 減少延遲

不論快取是否命中,memcache的響應時間都是影響總響應時間的重要因素。單個的網頁請求一般包含數百個memcache讀請求。如一個較火的頁面平均需要從memcache中獲取521個不同的資源。

為了減少資料庫等的負擔,我們準備了快取叢集,每個叢集都由數百臺memcache伺服器組成。資源個體經hash後存於不同的memcache伺服器中。因此,web伺服器必須請求多臺memcache伺服器,才能滿足使用者的請求。由此導致在很短的時間裡每個web伺服器都要和所有的memcache伺服器溝通。這種所有對所有的連線模式會導致潮湧堵塞(incast congestion)或者某臺伺服器不幸成為瓶頸。實時備份可以緩解這種狀況,但一般又會引起巨大的記憶體浪費。(譯者:為何?)

我們減少延遲的方法主要集中在memcache客戶端,每一個web伺服器都會執行memcache客戶端。這個客戶端提供一系列功能,包括:序列化、壓縮、請求路由、錯誤處理以及請求批處理。客戶端維護著一個對所以可獲得的伺服器的對映,對這個對映表的更新需要通過一個輔助的配置系統。

並行請求和批處理:我們構建web應用程式碼,目的是最小化對於頁面請求迴應所必要的網路往返數。我們構建了有向無環圖(DAG)用來表示資料間的依賴。web伺服器使用DAG來最大化可以併發讀取的專案數。平均來說,這些批量請求對於每個請求包含24個主鍵。

客戶端-伺服器通訊:memcached伺服器不會直接通訊。如果適當,我們將系統的複雜度嵌入無狀態的客戶端,而不是memcached伺服器。這極大地簡化了memcached,使我們專注於針對更有限的用例提供高效能。保持客戶端的無狀態使得我們可以快速迭代開發,同時也簡化了部署流程。客戶端的邏輯可以提供為兩種元件:可以嵌入應用的一個庫,或者做為一個名為mcrouter的獨立的代理程式。這個代理提供memcached伺服器的藉口,對不同伺服器之間的請求/回覆進行路由。

客戶端使用UDP和TCP協議與memcached伺服器通訊。我們依賴UDP來使請求的延遲和開銷縮減。因為UDP是無連線的,web伺服器中的每個執行緒都被允許直接與memcached伺服器通訊,通過mcrouter,不需要建立與維護連線因而減少了開銷。UDP實現了檢測出丟失的或失序接收(通過序列號)的包,並在客戶端將它們作為異常處理。它沒有提供任何試圖恢復的機制。在我們的基礎架構中,我們發現這個決定很實際。在峰值負載條件下,memcache客戶端觀察到0.25%的請求會被丟棄。其中大約80%是由於延遲或丟失包,其餘的是由於失序的交付。客戶端將異常作為快取不命中處理,但是web伺服器在查詢出資料以後,會跳過插入條目到memcached,以便避免對可能超載的網路會伺服器增添額外的負載。

圖 3: 經過mcrouter以後 UDP, TCP得到的延遲

Facebook 對 Memcache 伸縮性的增強原文請看這裡

推薦文章