Scaling Memcache in Facebook 筆記(二)
B. Region Replication
(Region感覺不好翻譯,類似於data center的概念吧)
前面我們談到把一堆Web Server和一堆Memcached作為一個Cluster。但是一個Cluster是不能無限Scale up的,因為每個Cluster裡面都是多對多的Connection,也就是說是N^2的connection上限,這樣N越大,就會:
- Hotkey 訪問量越來越大,最終搞得有hotkey的那個server hold不住
- 網路堵塞各種丟包
- ...
所以,論文提到建立多個Cluster (為了區分,後面把這種每個裡面包含一定數量的Web Server和memcached的cluster稱為Frontend Cluster),把它們和一個Storage Cluster組合起來,組成一個Region。所有Frontend Cluster共享同一個後端服務。
這樣相當於是用空間換時間:同一個key在每一個Frontend Cluster都可能有一個Copy,這樣會帶來consistency的問題(後面會講怎麼解決),但是這樣能夠降低latency和提高availability。
B.1 Regional Invalidation
這裡要解決的問題是:Cluster A的某個Server修改了後端資料庫裡面的值,如何把這個值被修改了的消息傳播到所有的Frontend Cluster,好讓它們把本地memcached對應的舊的數據清掉?
一種簡單的方式是讓那個做出修改的Web Server負責通知同一個Region里所有Cluster的所有memcached,但基於我們上面說到的種種理由,這樣performance會很差,而且還容易由於routing的配置出錯,所以這種操作只能現在在同一個Cluster里。
那麼對於其他Cluster,解決辦法是讓Storage Cluster來負責把消息廣播出去。Storage layer採用的是Mysql,而Mysql對於數據更新是有日誌的。第一步首先在日誌內容里加上對應的memcached key,然後設立一個進程監聽日誌,發現有數據更新,就解析出對應的key,然後把消息廣播給所有的memcached。
這裡有一點要注意的是我們不能讓資料庫直接跟memcached通信,原因包括但不限於:
- 這樣通信連接太多
- 這樣還得把routing邏輯放到後端邏輯里
所以每個cluster裡面有專門的server,運行mcrouter進程(還記得前面說過routing邏輯可以作為庫函數嵌入,也可以作為單獨的進程運行吧)。廣播消息會發送給這些進程,再由它們負責傳給本cluster的memcached。
B.2 Regional Pool
每個Frontend Cluster都有自己的memcached pools,我們姑且把它們稱作cluster pool吧。與之相對的Regional Pool顧名思義就是被所有cluster共享的memcached pool。為什麼要設立這樣的pool呢?
主要原因是,有一些數據,訪問頻率低,本身占內存還多,這樣的數據放到每個cluster里複製一份,要佔用很多額外的內存。所以把它們放到Regional Pool裡面就可以減少內存佔用。雖然Regional Pool的latency會相對更高(因為要穿越cluster的邊界),但是由於它們訪問頻率不高,所以也就顯得不那麼有所謂了。
論文提到目前是靠人的經驗來覺得什麼東西放Regional Pool的,不知道現在是不是做到自動化了。
B.3 Cold Cluster Warmup
當我們起一個新的cluster,或者把cluster拿去維護,等等等等之類的,這個cluster的cache基本是沒東西的,所以基本很大概率是各種cache miss,然後要等很久才能填得比較滿,而且這樣也會給後端服務帶來很大壓力。
解決辦法嘛,很容易就能想到,允許這個cold cluster在cache miss的時候,把別的「warm cluster」(就是cache有比較多數據的cluster)當作storage cluster去那邊讀數據,這樣原來需要幾天時間才能完成的warm up在幾個小時之內就能完成。
但是這樣又帶來了新的問題。想像下面這個情景:Cluster A某個Server往Storage里更新了一份數據,後端在完成數據更新後會把數據過期的消息發送到其他的Cluster。同時Cluster A里某個Server讀這份數據的時候發現cache miss,然後從Cluster B里讀;如果恰好Cluster B此時還沒有收到數據過期的消息(因為消息傳遞也是要時間的),這個讀會成功返回,然後這個Server拿到事實上已經過期的數據後會往Cluster A的memcached里寫。這樣子就相當於Cluster A的memcached就存放了一份過期的數據,而且這份過期的數據可能被保留很長甚至無限長的時間。
解決辦法是:memcached支持一個功能,在對一個key進行delete操作之後鎖住這個key一段時間不允許進行add操作。通過在cold cluster里設置這段hold-off時間為大於0的值(2秒),在上面的場景中,由於前面那個更新的Server是對本地memcached進行了delete操作的,第二個server拿著過期數據往裡寫的時候就會報錯,然後它就知道這個數據在Storage裡面有新值,就會去讀最新的值再往裡寫。理論上過期數據還是可能出現的,但是可能性大大減低了。
Scaling Memcache in Facebook 筆記(三)
引用:
Scaling Memcache at Facebook (Paper)
推薦閱讀:
※分散式系統理論進階 - Raft、Zab
※搞分散式,大數據都寫些什麼代碼,是不是寫不了幾行?
※bifrost : Rust 下的分散式系統框架
※基於分散式環境下限流系統的設計
※超級乾貨:Word2Vec課堂筆記(內附教學視頻)