某理財社區與微博Cache模型對比分析

作者:TomGE

轉自:微觀技術


前言

社交平臺由於內容成本較低,重度依賴用戶關係,實時互動、動態瀏覽。對系統整體性能要求較高。新浪微博由於較大的市場佔有率,用戶體量大,在這一領域有很多經驗,作者之前負責的理財社區業務與其有很多相似的地方,可以借鑑。下面會做一些比較分析。

一、緩存模型設計

• 已發表微博

可以使用 Redis 的 hash 來保存已發表微博。

一條微博通常包括多個字段,比如發表時間、發表用戶、正文內容等,通常使用微博 id 作爲 key 將多個鍵值對作爲 hash 保存在 Redis 中。

理財社區不象微博有字數限制,所以message字段可能比較大,目前限制最大字符10W,而且該字段的使用場景是在貼子詳情頁。

推薦頁或版塊列表頁只是展示一些摘要信息。所以內容正文拆成兩個維度,根據業務場景各取所需,避免每次大字段傳來傳去,浪費網絡帶寬。

計數模型會採用Hash散列的形式,以tid爲key,贊數、回覆數、瀏覽數作爲field。

下圖是消息提醒不同子類型業務的計數方式。貼子也是類似設計。

•  Feed流

當一個用戶訪問它的首頁信息流時候,他可以看到他所有關注用戶最新的信息。key 是當前用戶的 uid, 信息流的內容以 id --> timestamp 的形式保存在 zset 中,timestamp 用於排序,以便返回的列表是按照時間順序排列。微博的 id 用於業務下一步獲取微博的相關信息。

理財社區由於業務的特殊性,動態流有兩部分來源:人工推薦+訂閱的版塊熱門。

但每一塊的存儲結構採用zset,通過時間戳來排序,有一點區別是裏面的field,採用字符串,由“tid|uid”組成,方便後面可以並行取貼子和用戶信息。

• 關注與粉絲

我們可以把關注及粉絲庫也存在 zset 中,依舊使用 timestamp 來排序。key 是當前用戶 uid。

瞭解上述結構之後,我們繼續來看如何使用 Redis 來擴展整個系統,具備處理億級用戶的能力。

我們首先要做的,就是在 Redis 能夠存儲所有數據並且能夠正常地處理寫查詢的情況下,讓 Redis 的讀查詢處理能力超過單臺 Redis 服務器所能提供的讀查詢處理能力。

二、 擴展讀性能

假定我們用 Redis 構建一個與微博或 Twitter 具有相同特性和功能的社交網站,網站的其中一個特性就是允許用戶查看他們自己的 profile 頁和個人首頁信息流,每當用戶訪問時,程序就會從信息流裏面獲取大約 30 條內容。

因爲一臺專門負責獲取信息流的 Redis 服務器每秒至少可以同時爲 3,000 ~ 10,000 個用戶獲取信息流消息,所以這一操作對於規模較小的社交網站來說並不會造成什麼問題。

但是對於規模更大的社交網站來說,程序每秒需要獲取的信息流消息數量將遠遠超過單臺 Redis 服務器所能處理的上限,因此我們必須想辦法提升 Redis 每秒能夠獲取的信息流消息數量。

下面我們將會討論如何使用只讀的從服務器提升系統處理讀查詢的性能,使得系統的整體讀性能能夠超過單臺 Redis 服務器所能提供的讀查詢性能上限。

在對讀查詢的性能進行擴展,並將額外的服務器用作從服務器以提高系統處理讀查詢的性能之前,讓我們先來回顧一下 Redis 提高性能的幾個途徑。

    •在使用短結構時,請確保壓縮列表的最大長度不會太大以至於影響性能。

         •根據程序需要執行的查詢的類型,選擇能夠爲這種查詢提供最好性能的結構。比如說,不要把 LIST 當作 SET 使用;也不要獲取整個 HASH 然後在客戶端裏面對其進行排序,而是應該直接使用 ZSET;諸如此類。                 •在將大體積的對象緩存到 Redis 之前,考慮對它進行壓縮以減少讀取和寫入對象時所需的網絡帶寬。對比壓縮算法 lz4、gzip 和 bzip2,看看哪個算法能夠對被存儲的數據提供最好的壓縮效果和最好的性能。                •使用 pipeline(pipeline 是否啓用事務性質由具體的程序決定)以及連接池。

在做好了能確保讀查詢和寫查詢能夠快速執行的一切準備之後,接下來要考慮的就是如何實際解決“怎樣才能處理更多讀請求”這個正題。

提升 Redis 讀取能力的最簡單方法,就是添加提供讀能力的從服務器。

用戶可以運行一些額外的服務器,讓它們與主服務器進行連接,然後接受主服務器發送的數據副本並通過網絡進行準實時的更新(具體的更新速度取決於網絡帶寬)。通過將讀請求分散到不同的從服務器上面進行處理,用戶可以從新添加的從服務器上獲得額外的讀查詢處理能力。

記住:只對主服務器進行寫入

同時向多個從服務器發送快照的多個副本,可能會將主服務器可用的大部分帶寬消耗殆盡。使主服務器的延遲變高,甚至導致主服務器已經建立了連接的從服務器斷開。

解決從服務器重同步(resync)問題的其中一個方法,就是減少主服務器需要傳送給從服務器的數據數量,這可以通過構建樹狀複製中間層來完成。

加密和壓縮開銷

一般來說,使用 SSH 隧道帶來的加密開銷並不會給服務器造成大的負擔,因爲2.6 GHz 主頻的英特爾酷睿 2 單核處理器在只使用單個處理核心的情況下,每秒能夠使用 AES-128 算法加密 180MB 數據,而在使用 RC4 算法的情況下,每秒則可以加密大約 350MB 數據。在處理器足夠強勁並且擁有千兆網絡連接的情況下,程序即使在加密的情況下也能夠充分地使用整個網絡連接。

唯一可能會出問題的地方是壓縮—因爲 SSH 默認使用的是 gzip 壓縮算法。SSH 提供了配置選項,可以讓用戶選擇指定的壓縮級別(具體信息可以參考SSH的文檔),它的 1 級壓縮在使用之前提到的 2.6GHz 處理器的情況下,可以在複製的初始時候,以每秒 24~52MB 的速度對 Redis 的 RDB 文件進行壓縮;並在複製進入持續更新階段之後,以每秒 60~80MB 的速度對 Redis 的 AOF 文件進行壓縮。

三、擴展複雜的業務場景

•  對信息流列表進行分片

標題所說的“對信息流進行分片”實際上有些詞不達意,因爲首頁信息流和分組列表信息流通常都比較短(最大通常只有 1,000 條,實際的數量由 zset-max-ziplist-size 選項的值決定),因此實際上並不需要對信息流的內容進行分片;我們真正要做的是根據鍵名,把不同的信息流分別存儲到不同的分片上面。

另一方面,社交網站每個用戶 profile 信息流通常無限增長的。儘管絕大多數用戶每天最多隻會發布幾條微博,但也有話癆用戶以明顯高於這一頻率的速度發佈大量信息。以 Twitter 爲例,該網站上發佈信息最多的 1,000 個用戶,每人都發布了超過 150,000 條推文,而其中發佈最多的 15 個用戶,每人都發布了上百萬條推文。

從實用性的角度來看,一個合乎情理的做法是限制每個用戶的已發表微博最多隻能存儲大約 20,000 條信息,並將最舊的信息刪除或者隱藏,這種做法足以處理 99.999% 的 Twitter 用戶,而我們也會使用這一方案來對社交網站的個人信息流進行擴展。

• 通過分片對關注及粉絲列表擴展

雖然對信息流進行擴展的方法相當直觀易懂,但是對關注和粉絲列表這些由有序集合構成的“列表”進行擴展卻並不容易。這些有序集合絕大多數都很短(如 Twitter 上 99.99% 的用戶的關注者都少於 1,000 人),但是也存在少量用戶的列表非常大,他們關注了非常多的人或者擁有數量龐大的粉絲。

從實用性的角度來考慮,一個合理的做法是給用戶以及分組可以關注的人數設置一個上限(比如新浪微博普通用戶最大允許關注 2,000 用戶)。不過這個方法雖然可以控制用戶的關注人數,但是仍然解決不了單個用戶的粉絲數人數過多的問題。

目前理財社區也做了同樣的限制,一個用戶的關注數上限2000!

爲了處理關注和粉絲列表變得非常巨大的情況,我們需要將實現這些列表的有序集合劃分到多個分片上面,說得更具體一樣,也就是根據分片的數量把用戶的粉絲劃分爲多個部分,存在多個 zset 中。爲此,我們需要爲 ZADD 命令、ZREM 命令和 ZRANGEBYSCORE 命令實現特定的分片版本。

和信息流分片的區別是,這次分片的對象是數據而不是鍵。此外,爲了減少程序創建和調用連接的數量,把關注和粉絲的數據放置在同一個分片裏面將是一種非常有意義的做法。因此這次我們將使用新的方法對數據進行分片。

爲了能夠在關注及粉絲數據進行分片的時候,把兩者數據都存儲到同一個分片裏面,程序將會把關注者和被關注者雙方的 ID 用作查找分片鍵的其中一個參數。

•  按業務維度來拆分空間

以理財社區爲例,目前所有的業務都放在一個cache集羣裏,後面隨着業務的發展、數據量的增大、以及調用的頻率不同、各個業務域重要程度各不相同,肯定要按更細的維度來拆分。

比如貼子相關的(基本信息、正文、計數)獨立存儲空間;

用戶信息(基本信息、uid關係、用戶狀態等)獨立存儲空間

關注列表、粉絲列表等單獨空間;

各種營銷活動放在一塊;

熱文推薦

驚訝!緩存剛Put再Get居然獲取不到?

好機會,我要幫女同事解決Maven衝突問題

上線前一個小時,dubbo這個問題可把我折騰慘了

爲了控制Bean的加載我使出了這些殺手鐗

如有收穫,點個在看,誠摯感謝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章