成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Facebook Memcached 系統(tǒng)解決超大規(guī)模緩存一致性問(wèn)題

存儲(chǔ) 存儲(chǔ)架構(gòu)
希望今天的講解能幫助大家更深入地理解這個(gè)經(jīng)典的分布式緩存系統(tǒng)。這其中的很多設(shè)計(jì)思想,比如租約、CDC、Gutter 模式等,都是非常值得我們?cè)谧约旱捻?xiàng)目中借鑒的。

今天我們來(lái)深入聊一聊 Facebook 是如何將一個(gè)簡(jiǎn)單的開源緩存軟件 memcached,打造成一個(gè)能夠支撐起全球最大社交網(wǎng)絡(luò)、每秒處理數(shù)十億次請(qǐng)求的分布式緩存系統(tǒng)的。

我們會(huì)沿著 Facebook 的擴(kuò)展之路,從單個(gè)集群內(nèi)的優(yōu)化,到跨集群、跨地區(qū)的擴(kuò)展,逐步揭開其架構(gòu)設(shè)計(jì)的精妙之處。

為什么要用 Memcache?基礎(chǔ)架構(gòu)概覽

在開始之前,我們先解決一個(gè)基本問(wèn)題:Facebook 為什么不直接從數(shù)據(jù)庫(kù)(比如 MySQL)讀取數(shù)據(jù),而要費(fèi)這么大勁去構(gòu)建一個(gè)緩存系統(tǒng)?

答案很簡(jiǎn)單:  。對(duì)于 Facebook 這種讀多寫少(read-heavy)的業(yè)務(wù)場(chǎng)景,用戶的動(dòng)態(tài)、評(píng)論、好友列表等信息被瀏覽的次數(shù)遠(yuǎn)超被創(chuàng)建的次數(shù)。如果每次讀取都請(qǐng)求數(shù)據(jù)庫(kù),MySQL 完全扛不住如此巨大的讀取壓力。而 memcached 是一個(gè)完全基于內(nèi)存的鍵值存儲(chǔ)(key-value store),速度比基于磁盤的數(shù)據(jù)庫(kù)快幾個(gè)數(shù)量級(jí)。

核心架構(gòu):旁路緩存(Look-Aside Cache)

Facebook 使用 memcache 的模式非常經(jīng)典,稱為 旁路緩存 。流程如下:

  1. 讀(Read) :Web 服務(wù)器需要數(shù)據(jù)時(shí),首先會(huì)帶著一個(gè) key 去 memcache 里查找。
  • 緩存命中 (Cache Hit) :直接拿到數(shù)據(jù),返回給用戶。
  • 緩存未命中 (Cache Miss) :服務(wù)器會(huì)去后端數(shù)據(jù)庫(kù)(或其他服務(wù))查詢,拿到數(shù)據(jù)后,一方面返回給用戶,另一方面把這份數(shù)據(jù)用 set(key, value) 的方式寫回 memcache,方便下次讀取。
  1. 寫(Write) :當(dāng)數(shù)據(jù)發(fā)生變更時(shí),Web 服務(wù)器會(huì)先更新數(shù)據(jù)庫(kù)。為了保證緩存數(shù)據(jù)的一致性,它并 不是 去更新 memcache 里的值,而是直接向 memcache 發(fā)送一個(gè) delete(key) 命令,將舊的緩存數(shù)據(jù)刪除。

為什么寫操作是 delete 而不是 update

論文中提到,因?yàn)?nbsp;delete 是 冪等 (idempotent) 的。簡(jiǎn)單說(shuō),重復(fù)執(zhí)行多次 delete 和執(zhí)行一次的效果是一樣的,這在復(fù)雜的網(wǎng)絡(luò)環(huán)境中能簡(jiǎn)化系統(tǒng)設(shè)計(jì),避免因重試等機(jī)制導(dǎo)致數(shù)據(jù)錯(cuò)亂。而如果兩個(gè) update 請(qǐng)求因?yàn)榫W(wǎng)絡(luò)延遲亂序到達(dá),就可能導(dǎo)致緩存中存儲(chǔ)的是一個(gè)舊版本的數(shù)據(jù)。

(上面可能面臨陳舊數(shù)據(jù)寫入/臟數(shù)據(jù)的問(wèn)題,解決方案參考下文“減少負(fù)載:租約 (Leases) 的妙用”)

另外,需要明確兩個(gè)術(shù)語(yǔ):

  • memcached:指開源的、單機(jī)版的緩存軟件本身。
  • memcache:在本文中,特指 Facebook 基于 memcached 構(gòu)建的整個(gè)分布式緩存系統(tǒng)。

Memcached 與 Redis:現(xiàn)代緩存技術(shù)選型

我們來(lái)解決一個(gè)非常實(shí)際的工程問(wèn)題:memcached 在今天用得還多嗎?它和現(xiàn)在非常流行的 Redis 相比,我們應(yīng)該如何選擇?

首先,memcached 本身是一個(gè)單機(jī)的內(nèi)存緩存程序。我們常說(shuō)的“部署一個(gè) memcached 集群”,實(shí)際上是指通過(guò)客戶端的路由邏輯(或像 mcrouter 這樣的代理),將不同的 key 分發(fā)到不同的 memcached 實(shí)例上,從而在邏輯上構(gòu)成一個(gè)分布式系統(tǒng)。

在今天的技術(shù)選型中,Redis 因其豐富的功能和靈活性,在很多場(chǎng)景下已經(jīng)成為首選。但 memcached 憑借其極致的簡(jiǎn)潔和性能,依然在特定領(lǐng)域(尤其是需要大規(guī)模、低延遲純緩存的場(chǎng)景)擁有一席之地。它們的對(duì)比可以總結(jié)如下:

特性

Memcached

Redis

核心定位

純粹的內(nèi)存緩存 (Cache)

內(nèi)存數(shù)據(jù)結(jié)構(gòu)服務(wù)器 (In-Memory Data Store)

數(shù)據(jù)類型

只支持簡(jiǎn)單的字符串 (Key-Value)。

支持字符串、列表、哈希、集合、有序集合等豐富的數(shù)據(jù)結(jié)構(gòu)。

性能

多線程

 架構(gòu)。在簡(jiǎn)單的 get/set 場(chǎng)景和高并發(fā)下,性能極高,CPU 利用率更好。Facebook 還對(duì)其做了進(jìn)一步的性能優(yōu)化。

單線程

 執(zhí)行命令(I/O 多路復(fù)用)。避免了鎖的開銷,但CPU密集型操作會(huì)阻塞其他請(qǐng)求。Redis 6.0 后引入了多線程 I/O。

持久化

不支持

。數(shù)據(jù)只存在于內(nèi)存,服務(wù)器重啟后數(shù)據(jù)全部丟失,純粹用于緩存。

支持

 。提供 RDB(快照)和 AOF(日志)兩種持久化方式,可作為數(shù)據(jù)庫(kù)使用。

高級(jí)功能

功能極簡(jiǎn),只有 getsetdelete 等基本操作。

支持事務(wù)、發(fā)布/訂閱 (Pub/Sub)、Lua 腳本、Streams 等高級(jí)功能。

內(nèi)存管理

使用 Slab Allocator 。預(yù)先分配固定大小的內(nèi)存塊,可能因數(shù)據(jù)大小與塊大小不匹配而造成一定的空間浪費(fèi)。

內(nèi)存管理更精細(xì),支持多種淘汰策略。

選型建議:

  • 選擇 Memcached :如果你的需求非常純粹,就是一個(gè)高速、高并發(fā)、分布式的鍵值緩存,且不需要復(fù)雜的數(shù)據(jù)結(jié)構(gòu)和持久化。它的簡(jiǎn)潔性和多線程帶來(lái)的高吞吐在某些大規(guī)模場(chǎng)景下是巨大優(yōu)勢(shì)。
  • 選擇 Redis :絕大多數(shù)場(chǎng)景下的更優(yōu)選擇。如果你需要使用哈希、列表等數(shù)據(jù)結(jié)構(gòu),或者需要消息隊(duì)列、排行榜(有序集合)、分布式鎖等功能,Redis 提供了開箱即用的解決方案。

集群之內(nèi):攻克延遲與負(fù)載

當(dāng)服務(wù)器規(guī)模擴(kuò)展到數(shù)千臺(tái)時(shí),即便都在一個(gè)數(shù)據(jù)中心內(nèi),也會(huì)遇到巨大的挑戰(zhàn)。Facebook 的優(yōu)化主要集中在兩個(gè)方面:降低訪問(wèn)延遲和減少緩存未命中帶來(lái)的負(fù)載。

降低延遲:解決全連接與 Incast 擁塞

在一個(gè)集群中,一臺(tái) Web 服務(wù)器為了響應(yīng)單次用戶請(qǐng)求,可能需要與成百上千臺(tái) memcached 服務(wù)器通信,這被稱為“ 全連接 (all-to-all) ”的通信模式。這種模式極易引發(fā)網(wǎng)絡(luò)擁堵,尤其是 Incast 擁塞(當(dāng)大量服務(wù)器同時(shí)向一個(gè)目標(biāo)發(fā)送數(shù)據(jù)時(shí),會(huì)導(dǎo)致交換機(jī)緩沖區(qū)溢出而大量丟包)。

Facebook 的解決方案是從客戶端和服務(wù)端兩方面入手:

客戶端并行與批量請(qǐng)求

Web 服務(wù)器的客戶端庫(kù)會(huì)將一次頁(yè)面請(qǐng)求所需的所有數(shù)據(jù)依賴關(guān)系構(gòu)建成一個(gè)有向無(wú)環(huán)圖(DAG),從而最大限度地并行發(fā)起請(qǐng)求 。同時(shí),它會(huì)將多個(gè) key 的請(qǐng)求合并成一個(gè)批量請(qǐng)求(batched request),平均一次會(huì)打包 24 個(gè) key 。

這里提到的 DAG(有向無(wú)環(huán)圖),它并 不是 由緩存系統(tǒng)自動(dòng)生成的,而是 由應(yīng)用程序的業(yè)務(wù)邏輯定義的 。

舉個(gè)例子,渲染一個(gè)用戶個(gè)人主頁(yè),可能需要以下數(shù)據(jù):

  1. 用戶基本信息(user_info
  2. 用戶的好友列表(friend_list
  3. 用戶的最新動(dòng)態(tài)(latest_posts

這三者都依賴于 user_id。一旦獲取到 user_id,這三份數(shù)據(jù)之間沒(méi)有依賴關(guān)系,可以并行去獲取。應(yīng)用框架會(huì)根據(jù)代碼中聲明的這種依賴關(guān)系,構(gòu)建出一個(gè) DAG,然后調(diào)度 memcache 客戶端盡可能地并行執(zhí)行獨(dú)立的請(qǐng)求分支,同時(shí)將同一分支上的多個(gè)請(qǐng)求打包,從而最小化網(wǎng)絡(luò)往返次數(shù)和等待時(shí)間。

協(xié)議選擇與連接優(yōu)化

  • 對(duì)于 get 請(qǐng)求,使用 UDP 協(xié)議。UDP 無(wú)連接的特性減少了建立和維護(hù) TCP 連接的開銷,從而降低了延遲。雖然 UDP 不可靠,但 Facebook 的網(wǎng)絡(luò)設(shè)施質(zhì)量很高,實(shí)踐中只有約 0.25% 的 get 請(qǐng)求被丟棄,客戶端直接將這些視為緩存未命中即可。
  • 對(duì)于 set 和 delete 這類必須保證可靠性的操作,則通過(guò) TCP 完成。為了避免海量 Web 線程與 memcached 服務(wù)器建立過(guò)多 TCP 連接,他們引入了一個(gè)名為 mcrouter 的代理進(jìn)程。mcrouter 部署在 Web 服務(wù)器本地,負(fù)責(zé)將來(lái)自多個(gè)線程的 TCP 請(qǐng)求聚合起來(lái),再統(tǒng)一轉(zhuǎn)發(fā)給 memcached 服務(wù)器,極大地減少了連接數(shù)和服務(wù)器資源消耗。

客戶端流量控制

為了解決 Incast 擁塞,客戶端實(shí)現(xiàn)了一個(gè)滑動(dòng)窗口(sliding window)機(jī)制來(lái)控制未完成的請(qǐng)求數(shù)量。當(dāng)窗口過(guò)小,請(qǐng)求串行化會(huì)增加延遲;當(dāng)窗口過(guò)大,則會(huì)引發(fā)網(wǎng)絡(luò)擁堵和丟包。通過(guò)動(dòng)態(tài)調(diào)整窗口大小,可以在兩者之間找到一個(gè)平衡點(diǎn)。

減少負(fù)載:租約 (Leases) 的妙用

緩存未命中會(huì)給后端數(shù)據(jù)庫(kù)帶來(lái)巨大壓力。Facebook 引入了一種非常巧妙的機(jī)制—— 租約 (Leases) ,它一舉解決了兩個(gè)老大難問(wèn)題: 陳舊數(shù)據(jù)寫入(stale sets)和驚群效應(yīng)(thundering herds) 。

問(wèn)題一:陳舊數(shù)據(jù)寫入 (Stale Sets)

想象這個(gè)場(chǎng)景:

  1. 客戶端 C1 請(qǐng)求 key,緩存未命中。
  2. C1 去數(shù)據(jù)庫(kù)查詢,得到 value=1。但此時(shí) C1 因?yàn)槟承┰蚩D了一下。
  3. 在此期間,另一個(gè)用戶更新了數(shù)據(jù),數(shù)據(jù)庫(kù)中 key 的值變?yōu)?nbsp;2,并刪除了緩存。
  4. 客戶端 C2 也請(qǐng)求 key,緩存未命中,從數(shù)據(jù)庫(kù)拿到 value=2,并成功寫入緩存。
  5. 卡頓的 C1 終于恢復(fù),將它持有的舊數(shù)據(jù) value=1 寫入了緩存,覆蓋了新數(shù)據(jù)。

此時(shí),緩存中就存在了一個(gè)臟數(shù)據(jù)。

問(wèn)題二:驚群效應(yīng) (Thundering Herds)/緩存雪崩

當(dāng)一個(gè)熱點(diǎn) key(比如首頁(yè)上的熱門新聞)突然失效(被更新或刪除),成千上萬(wàn)的請(qǐng)求會(huì)同時(shí)穿透緩存,打到數(shù)據(jù)庫(kù)上,可能瞬間壓垮數(shù)據(jù)庫(kù)。

租約的解決方案

租約是 memcached 在處理緩存未命中時(shí),返回給客戶端的一個(gè) 64 位 令牌 (token) 。

解決 Stale Sets

客戶端在 set 數(shù)據(jù)時(shí)必須帶上這個(gè)租約令牌。memcached 會(huì)驗(yàn)證令牌的有效性。在上面的例子中,當(dāng)數(shù)據(jù)被更新時(shí),memcached 會(huì)將與該 key 相關(guān)的租約(C1 持有的)標(biāo)記為無(wú)效。因此,當(dāng) C1 帶著過(guò)期的租約來(lái)寫緩存時(shí),請(qǐng)求會(huì)被直接拒絕,從而保證了緩存不會(huì)被舊數(shù)據(jù)污染。這類似于一種 樂(lè)觀鎖 或 load-link/store-conditional 操作。

解決 Thundering Herds

memcached 服務(wù)器會(huì)對(duì)租約的發(fā)放進(jìn)行 速率限制 ,例如,針對(duì)同一個(gè) key,10 秒內(nèi)只發(fā)放一個(gè)租約。這樣,當(dāng)熱點(diǎn) key 失效時(shí),只有第一個(gè)請(qǐng)求的客戶端能拿到租約并去查詢數(shù)據(jù)庫(kù)。其他客戶端在 10 秒內(nèi)再次請(qǐng)求時(shí),會(huì)收到一個(gè)特殊通知,告訴它們“稍等片刻再試”。通常,第一個(gè)客戶端在毫秒級(jí)時(shí)間內(nèi)就能把新數(shù)據(jù)寫回緩存。后續(xù)的客戶端重試時(shí),就能直接命中緩存了。通過(guò)這種方式,原本成千上萬(wàn)的數(shù)據(jù)庫(kù)請(qǐng)求被縮減到只有一次,效果極其顯著。

故障處理:Gutter 服務(wù)器

如果一臺(tái) memcached 服務(wù)器宕機(jī),所有指向它的請(qǐng)求都會(huì)失敗,這同樣會(huì)形成一股流量洪峰沖擊數(shù)據(jù)庫(kù)。

如何處理緩存節(jié)點(diǎn)宕機(jī)?

一個(gè)常見的想法是將宕機(jī)節(jié)點(diǎn)上的 key 通過(guò)哈希算法重新分布到其他健康的節(jié)點(diǎn)上。但 Facebook 指出這是 危險(xiǎn)的 ,因?yàn)槿绻硞€(gè) key 是熱點(diǎn) key,它可能會(huì)將大量請(qǐng)求帶到新的節(jié)點(diǎn)上,導(dǎo)致該節(jié)點(diǎn)也被壓垮,從而引發(fā) 級(jí)聯(lián)故障 (cascading failures) 。

Facebook 的方案是設(shè)立一個(gè)專門的、小規(guī)模的服務(wù)器池,叫做 Gutter 。這些服務(wù)器平時(shí)基本是空閑的。當(dāng)客戶端訪問(wèn)一臺(tái) memcached 服務(wù)器超時(shí)后,它不會(huì)重新哈希,而是會(huì)把請(qǐng)求重試到 Gutter 服務(wù)器上。Gutter 作為一個(gè)臨時(shí)的緩存替代品,接管了失效服務(wù)器的負(fù)載,從而保護(hù)了后端數(shù)據(jù)庫(kù)。Gutter 中緩存項(xiàng)的過(guò)期時(shí)間很短,以避免數(shù)據(jù)一致性問(wèn)題。

這引出了另一個(gè)問(wèn)題:為什么 Gutter 服務(wù)器要保持空閑,而不是也用來(lái)提供普通服務(wù)?因?yàn)楫?dāng)一臺(tái)服務(wù)器故障時(shí),接替它的 Gutter 服務(wù)器需要擁有足夠的、未被占用的處理能力來(lái)承接前者的全部負(fù)載。

區(qū)域之內(nèi):跨集群復(fù)制

隨著業(yè)務(wù)增長(zhǎng),單個(gè)集群的規(guī)模總有上限。Facebook 將一個(gè)數(shù)據(jù)中心劃分為多個(gè) 前端集群 (frontend clusters) ,它們共享一個(gè) 存儲(chǔ)集群 (storage cluster) (即數(shù)據(jù)庫(kù))。這個(gè)整體被稱為一個(gè) 區(qū)域 (region) 。

這種架構(gòu)下,最大的挑戰(zhàn)是如何處理跨集群的數(shù)據(jù)復(fù)制和一致性問(wèn)題。

區(qū)域性失效 (Regional Invalidations)

由于每個(gè)前端集群都有自己獨(dú)立的 memcache,一份數(shù)據(jù)可能被緩存在多個(gè)集群中。當(dāng)數(shù)據(jù)庫(kù)中的數(shù)據(jù)更新時(shí),必須通知所有集群刪除對(duì)應(yīng)的舊緩存。

這個(gè)任務(wù)由一個(gè)叫 mcsqueal 的守護(hù)進(jìn)程完成。它部署在數(shù)據(jù)庫(kù)服務(wù)器上,通過(guò)實(shí)時(shí)監(jiān)控?cái)?shù)據(jù)庫(kù)的提交日志(binlog),來(lái)捕獲數(shù)據(jù)變更操作。一旦捕獲到變更,mcsqueal 就會(huì)解析出需要失效的 memcache key,然后將刪除請(qǐng)求廣播給該區(qū)域內(nèi)所有前端集群的 memcache 。

這種基于 變更數(shù)據(jù)捕獲 (Change Data Capture, CDC) 的異步失效模式,比讓 Web 服務(wù)器直接廣播失效請(qǐng)求更可靠、更高效,因?yàn)樗芨玫嘏刻幚碚?qǐng)求,并且基于可靠的數(shù)據(jù)庫(kù)日志,可以重放丟失的失效消息。

區(qū)域性失效:mcsqueal 的可靠之道

mcsqueal 詳細(xì)機(jī)制:基于 變更數(shù)據(jù)捕獲 (Change Data Capture, CDC) 的系統(tǒng)是保證跨集群緩存一致性的基石,其流程遠(yuǎn)比 Web 服務(wù)器直接發(fā) delete 請(qǐng)求要精妙和可靠。

詳細(xì)流程如下:

  1. 修改數(shù)據(jù)庫(kù)事務(wù) :當(dāng) Web 服務(wù)器要更新數(shù)據(jù)時(shí),它發(fā)給數(shù)據(jù)庫(kù)的事務(wù)不僅僅是 UPDATE 或 INSERT 語(yǔ)句。它還會(huì)附加元數(shù)據(jù),明確指出這個(gè)數(shù)據(jù)庫(kù)變更會(huì)影響到哪些 memcache 的 key 。
  2. 寫入可靠日志 :數(shù)據(jù)庫(kù)執(zhí)行事務(wù),并將數(shù)據(jù)變更和這些需要失效的 key 一同記錄到其高可靠的提交日志中(例如 MySQL 的 binlog)。這是最關(guān)鍵的一步,因?yàn)槿罩臼浅志没矣行虻摹?/span>
  3. mcsqueal 監(jiān)控與解析 :mcsqueal 守護(hù)進(jìn)程像一個(gè)忠實(shí)的哨兵,持續(xù)不斷地監(jiān)控(tail)數(shù)據(jù)庫(kù)的提交日志。當(dāng)它讀到新的變更記錄時(shí),就會(huì)從中解析出那些需要被刪除的 key
  4. 廣播與中繼 :mcsqueal 將提取出的刪除請(qǐng)求,批量打包后,廣播給區(qū)域內(nèi)所有前端集群。這些請(qǐng)求并非直接打到 memcached 服務(wù)器上,而是先發(fā)送給每個(gè)集群中專門負(fù)責(zé)中繼的 mcrouter 實(shí)例。
  5. mcrouter 精準(zhǔn)投遞 :mcrouter 收到批量包后,將其解開,并根據(jù)每個(gè) key 的哈希值,將刪除命令精準(zhǔn)地路由到該 key 所在的具體 memcached 服務(wù)器上。

這套機(jī)制的核心優(yōu)勢(shì)在于 可靠性 和 效率 。因?yàn)槭е噶钣涗浽跀?shù)據(jù)庫(kù)的持久化日志里,即使中途 mcsqueal 進(jìn)程崩潰或者網(wǎng)絡(luò)中斷,恢復(fù)后只需從上次中斷的位置繼續(xù)讀取日志即可,保證了失效消息“ 不丟不重 ” 。同時(shí),高效的批量處理也極大地降低了網(wǎng)絡(luò)開銷。

冷集群預(yù)熱 (Cold Cluster Warmup)

當(dāng)一個(gè)新的前端集群上線,或者一個(gè)集群因維護(hù)重啟時(shí),它的緩存是空的(稱為“冷集群”),此時(shí)所有請(qǐng)求都會(huì)穿透到數(shù)據(jù)庫(kù),造成巨大沖擊。

為了解決這個(gè)問(wèn)題,F(xiàn)acebook 設(shè)計(jì)了 冷集群預(yù)熱 機(jī)制。簡(jiǎn)單來(lái)說(shuō),就是讓冷集群里的客戶端在緩存未命中時(shí),不去請(qǐng)求數(shù)據(jù)庫(kù),而是去請(qǐng)求一個(gè)緩存數(shù)據(jù)齊全的“熱集群”的 memcache 。這相當(dāng)于用其他集群的緩存來(lái)幫助新集群“預(yù)熱”。為了處理這期間可能發(fā)生的競(jìng)爭(zhēng)問(wèn)題(例如,數(shù)據(jù)剛在熱集群被更新,冷集群就來(lái)讀取舊數(shù)據(jù)),他們?cè)谙蚶浼喊l(fā)送 delete 命令時(shí),會(huì)附加一個(gè)短暫的“ 拒絕寫入 ”時(shí)間(如 2 秒)。這能有效防止臟數(shù)據(jù)被寫入冷集群的緩存中。

當(dāng)一個(gè)新集群(冷集群)上線時(shí),讓它從一個(gè)已有集群(熱集群)的緩存中拉取數(shù)據(jù)預(yù)熱,是一個(gè)非常聰明的辦法。但如何處理競(jìng)爭(zhēng)問(wèn)題,以及 2 秒的“拒絕寫入”時(shí)間是怎么回事?

預(yù)熱機(jī)制與競(jìng)爭(zhēng)問(wèn)題

預(yù)熱的基本流程是:冷集群的客戶端在緩存未命中后,會(huì)向熱集群發(fā)送 get 請(qǐng)求,拿到數(shù)據(jù)后再嘗試 add 到本地緩存中。

這里的競(jìng)爭(zhēng)在于:在冷集群客戶端從“獲取數(shù)據(jù)”到“寫入本地緩存”的這個(gè)時(shí)間窗口內(nèi),原始數(shù)據(jù)可能已經(jīng)被更新了。此時(shí),失效消息會(huì)同時(shí)發(fā)往熱集群和冷集群。如果冷集群的客戶端動(dòng)作不夠快,就可能把已經(jīng)過(guò)期的舊數(shù)據(jù)寫入本地緩存。

“拒絕寫入”的妙用

為了解決這個(gè)問(wèn)題,F(xiàn)acebook 對(duì) delete 命令做了擴(kuò)展。發(fā)往冷集群的失效刪除命令,會(huì)附帶一個(gè)參數(shù),即 拒絕寫入期 (hold-off time) ,默認(rèn)為 2 秒。

當(dāng)冷集群的 memcached 服務(wù)器收到這樣一條命令后,它會(huì):

  1. 刪除指定的 key
  2. 在該 key 上設(shè)置一個(gè) 臨時(shí)鎖 ,在接下來(lái)的 2 秒內(nèi), 拒絕 所有針對(duì)這個(gè) key 的 add 或 set 操作。

現(xiàn)在我們?cè)倏搭A(yù)熱流程:當(dāng)冷集群客戶端帶著從熱集群取回的舊數(shù)據(jù)嘗試 add 到本地時(shí),如果一個(gè)失效消息剛剛到達(dá),這個(gè) add 操作就會(huì)因?yàn)檫@個(gè) 2 秒的鎖而 失敗 。

這個(gè)失敗是一個(gè)非常重要的信號(hào),它告訴客戶端:“你手上的數(shù)據(jù)已經(jīng)舊了!” 客戶端捕獲到這個(gè)失敗后,就會(huì)放棄這次預(yù)熱操作,轉(zhuǎn)而直接從權(quán)威數(shù)據(jù)源——數(shù)據(jù)庫(kù)——去獲取最新的數(shù)據(jù)。

為什么是 2 秒?如果延遲更長(zhǎng)怎么辦?

  • 為什么是 2 秒? 這是一個(gè)基于海量實(shí)踐數(shù)據(jù)得出的 工程經(jīng)驗(yàn)值 (heuristic) 。2 秒被認(rèn)為足以覆蓋絕大多數(shù)情況下,失效消息在數(shù)據(jù)中心內(nèi)部網(wǎng)絡(luò)中的傳播和處理延遲。
  • 如果延遲超過(guò) 2 秒呢? 論文坦誠(chéng)地承認(rèn),這種情況理論上是可能發(fā)生的。如果一條失效消息因?yàn)闃O端網(wǎng)絡(luò)擁堵等原因,延遲超過(guò)了 2 秒才到達(dá),那么一個(gè) stale 的值的確可能被成功寫入緩存。

但這正是 Facebook 架構(gòu)哲學(xué)的一個(gè)完美體現(xiàn):他們?cè)敢饨邮苓@種極小概率的、短暫的數(shù)據(jù)不一致,來(lái)?yè)Q取 巨大的運(yùn)維收益 ——將一個(gè)集群的預(yù)熱時(shí)間從幾天縮短到幾小時(shí)。這是一種在“一致性”和“可用性/性能”之間做出的清醒且務(wù)實(shí)的權(quán)衡。

跨越區(qū)域:全球一致性挑戰(zhàn)

為了降低全球用戶的訪問(wèn)延遲和容災(zāi),F(xiàn)acebook 在世界各地部署了多個(gè)區(qū)域(數(shù)據(jù)中心)。架構(gòu)上,他們?cè)O(shè)定一個(gè) 主區(qū)域 (master region) ,存放主數(shù)據(jù)庫(kù),其他區(qū)域作為 從區(qū)域 (replica regions) ,存放只讀的數(shù)據(jù)庫(kù)副本。數(shù)據(jù)通過(guò) MySQL 的復(fù)制機(jī)制從主區(qū)域同步到從區(qū)域。

這里最大的挑戰(zhàn)是 復(fù)制延遲 (replication lag) 。如果一個(gè)歐洲用戶更新了他的頭像(寫請(qǐng)求被路由到美國(guó)的主區(qū)域),然后立即刷新頁(yè)面(讀請(qǐng)求在本地的歐洲從區(qū)域),他很可能因?yàn)閺?fù)制延遲而看不到自己的更新。

為了解決這個(gè)問(wèn)題,F(xiàn)acebook 使用了一種叫 遠(yuǎn)程標(biāo)記 (remote marker) 的機(jī)制。 當(dāng)一個(gè)寫請(qǐng)求發(fā)生在從區(qū)域時(shí):

  1. 客戶端首先在 本地memcache 中設(shè)置一個(gè)標(biāo)記 key(如 remote_marker_for_key_X)。
  2. 然后將寫請(qǐng)求發(fā)送到 主區(qū)域 的數(shù)據(jù)庫(kù)。
  3. 同時(shí)刪除 本地memcache 中真正的 key_X 。

當(dāng)后續(xù)的讀請(qǐng)求來(lái)到從區(qū)域:

  1. 它首先請(qǐng)求 key_X,緩存未命中。
  2. 接著它會(huì)檢查是否存在 remote_marker_for_key_X
  3. 如果標(biāo)記存在,說(shuō)明這個(gè) key 剛剛在主區(qū)域被更新,本地?cái)?shù)據(jù)庫(kù)副本的數(shù)據(jù)可能是舊的。于是,客戶端會(huì)將這次讀請(qǐng)求 直接路由到主區(qū)域 去查詢最新數(shù)據(jù),從而避免讀到舊數(shù)據(jù)。

這是一種典型的 用延遲換取一致性 的權(quán)衡。雖然跨區(qū)域讀取會(huì)慢一些,但保證了更好的用戶體驗(yàn)(直接請(qǐng)求美國(guó)數(shù)據(jù)比用戶等待數(shù)據(jù)復(fù)制到歐洲區(qū)域更快)。

總結(jié)與啟示

Facebook 的 Memcache 架構(gòu)是一個(gè)在工程實(shí)踐中不斷演化、充滿權(quán)衡的杰作。從它的演進(jìn)中,我們可以學(xué)到幾個(gè)核心思想,這對(duì)我們?nèi)粘5南到y(tǒng)設(shè)計(jì)和面試都大有裨益:

  1. 分離關(guān)注點(diǎn) :將緩存系統(tǒng)和持久化存儲(chǔ)系統(tǒng)分離,使得兩者可以獨(dú)立擴(kuò)展和優(yōu)化。
  2. 將復(fù)雜性推向客戶端 :memcached 服務(wù)器本身極其簡(jiǎn)單,大部分復(fù)雜邏輯如路由、序列化、錯(cuò)誤處理、服務(wù)發(fā)現(xiàn)等都放在了無(wú)狀態(tài)的客戶端(或 mcrouter)中。這使得系統(tǒng)核心組件非常穩(wěn)定,而客戶端邏輯可以快速迭代和部署。
  3. 為失敗而設(shè)計(jì) :從 Gutter 系統(tǒng)到 mcsqueal 的可靠重放,整個(gè)系統(tǒng)處處體現(xiàn)著對(duì)故障的思考,致力于避免單點(diǎn)故障和級(jí)聯(lián)故障。
  4. 簡(jiǎn)單為王 :系統(tǒng)設(shè)計(jì)中充滿了務(wù)實(shí)的權(quán)衡,例如容忍短暫的陳舊數(shù)據(jù)以換取極高的性能和可用性,避免引入過(guò)于復(fù)雜但收益有限的優(yōu)化。

希望今天的講解能幫助大家更深入地理解這個(gè)經(jīng)典的分布式緩存系統(tǒng)。這其中的很多設(shè)計(jì)思想,比如租約、CDC、Gutter 模式等,都是非常值得我們?cè)谧约旱捻?xiàng)目中借鑒的。


責(zé)任編輯:武曉燕 來(lái)源: Piper蛋窩
相關(guān)推薦

2022-09-06 15:30:20

緩存一致性

2024-04-11 13:45:14

Redis數(shù)據(jù)庫(kù)緩存

2024-11-14 07:10:00

2019-02-13 11:04:42

系統(tǒng)緩存軟件

2016-11-29 09:00:19

分布式數(shù)據(jù)一致性CAS

2021-09-23 10:20:16

算法模型技術(shù)

2016-12-14 11:44:25

阿里Docker大數(shù)據(jù)

2023-04-13 08:15:47

Redis緩存一致性

2020-07-23 14:03:09

數(shù)據(jù)中心數(shù)據(jù)網(wǎng)絡(luò)

2020-09-04 06:32:08

緩存數(shù)據(jù)庫(kù)接口

2020-02-10 08:00:38

AI 數(shù)據(jù)人工智能

2022-08-11 07:55:05

數(shù)據(jù)庫(kù)Mysql

2022-06-21 21:47:13

數(shù)據(jù)系統(tǒng)

2021-09-08 11:03:13

緩存數(shù)據(jù)庫(kù)性能

2020-12-11 19:52:06

數(shù)據(jù)中心超大規(guī)模數(shù)據(jù)中心

2023-02-14 11:24:36

2011-12-16 09:54:17

網(wǎng)絡(luò)架構(gòu)網(wǎng)絡(luò)架構(gòu)系統(tǒng)架構(gòu)系統(tǒng)

2025-02-26 08:30:00

2022-12-30 14:14:51

數(shù)據(jù)中心服務(wù)器

2012-09-24 09:35:42

分布式系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 在线观看成人免费视频 | 亚洲一区视频 | 欧洲毛片| 午夜视频在线 | 免费视频一区 | 国产成人短视频在线观看 | 国产一区高清 | 91精品国产乱码久久久久久久久 | 一区二区三区视频免费看 | 亚洲一区导航 | 欧美日韩国产一区二区三区 | 狠狠干天天干 | 国产精品视频在线播放 | www.蜜桃av | 久草视频2 | 国产夜恋视频在线观看 | 国产激情自拍视频 | 免费在线观看一区二区 | 精品国产乱码久久久久久丨区2区 | 久久99国产精品 | 免费看色 | 一区二区三区视频 | 欧美国产精品一区二区三区 | 久久久久久一区 | 国产日韩精品一区二区 | 狠狠艹| 玖玖爱365| 国产超碰人人爽人人做人人爱 | 亚洲大片一区 | 在线国产视频观看 | 自拍 亚洲 欧美 老师 丝袜 | 精国产品一区二区三区四季综 | 国产精品一区二区三区免费观看 | 97avcc| av香港经典三级级 在线 | 精品国产欧美一区二区三区成人 | 久久欧美高清二区三区 | 亚州毛片 | 亚洲欧美日韩中文字幕一区二区三区 | 一区二区中文字幕 | 成人午夜精品 |