聊聊 Twitter 的架構決策

研究大規模互聯網應用可以學到很多東西,像 Netflix 這樣的公司,其複雜的架構不僅能夠向數百萬用戶提供內容,而且還改善了用戶體驗,增加了用戶粘性。

在一家小公司中,像推送通知這樣的機制可以是主代碼庫的一部分,並且可以在同一臺機器上與應用程序的其他部分一起運行。

但在 Netflix,負責推送通知的服務器部署在全球多個數據中心,在本文的最後有一個關於這一架構演進的詳細討論的鏈接。

雖然學習這些案例研究很有趣,但大多數人仍然沒有在如此大的規模下工作的經驗。在某些情況下,我們更容易瞭解公司如何從傳統的主流應用服務方式轉變爲迎合用戶體驗的現代解決方案。

下面我們以 Twitter 爲例加以說明。

10 年前,大約在 2012 - 2013 年,Twitter 呈指數級增長,服務 1.5 億用戶。

當時 Twitter 每秒鐘處理 6000 條來自用戶的推文。我想說的是,這仍然在可控範圍之內,我們擁有高吞吐量的數據庫可以處理這一寫入速率。

但對於閱讀推文的用戶來說,就有問題了。當每個用戶查詢 Twitter 主頁時,必須獲取到所關注的每個人的推文組合,這就是每秒鐘 30 萬請求!

如果直接從數據庫讀取,這個讀取速率會讓人發瘋!

每個請求是什麼樣子?我舉個例子。想象一個人關注了 5000 個用戶,當他打開 Twitter 網站時,後端使用如下查詢訪問數據庫:

Get all the tweets ordered according to time, if it belongs to any of these 5000 users
如果某個用戶在這5000個用戶內,獲取該用戶所有的推文,並按時間排序

即使將查詢限制爲幾條推文,仍然會給數據庫帶來沉重的負擔。

Twitter 如何解決容量問題🚀

Twitter 有兩條時間線:

在設計數據庫時,最簡單的數據庫只是將所有寫操作附加到文件的末尾,並在需要時讀取。沒有什麼比簡單的附加到文件中更快的了,但是,隨着數據庫文件的增長,從這種類型的數據庫中查詢某些內容將花費很長時間。

爲了減少查詢時間,我們在其中添加索引。但是添加索引將意味着寫入將花費更長的時間,因爲必須在將其寫入數據庫之前編輯索引。但由於讀取的數量將遠遠多於寫入的數量,這是一種公平的權衡。

同樣,Twitter 的閱讀量也遠遠超過了寫入量。所以他們構建了一個系統,可以更好的爲用戶的主頁時間線服務。他們預先計算出所有用戶的主頁時間線,並將其存儲在 Redis 集羣中。就這麼簡單!

現在,無論用戶何時發佈消息,該消息都會被插入每個關注者的時間線隊列中。所以如果你有 5000 個粉絲,你的推文就會有 5000 個寫操作!外加上 1 個數據庫本身的寫操作😅

這聽起來很瘋狂,但你仔細想想,這是有道理的。現在可以同時爲數百萬用戶提供服務而不需要訪問磁盤,這可以從根本上減少延遲。這個過程叫做扇出(fan-out)!

  1. 那麼到目前爲止,Twitter 的架構是怎樣的呢?

  2. 用戶訪問 Twitter 的主頁。

  3. Twitter 在 Redis 集羣中查找用戶的主頁時間線。

  4. 一旦找到,就向用戶直接展示。

當用戶發送一條推文時,該推文會被複制到用戶的所有關注者的時間線上。

還有一些其他的細節:

  1. Twitter 維護了一個圖數據庫,其保存了用戶關注關係的所有數據。當發生扇出時,扇出服務查詢這個圖數據庫以確定將推文推送到何處。

  2. 扇出將在數據中心的 3 臺不同的機器上進行復制,即每個用戶的時間線存儲在 3 臺不同的機器上。這是必需的,因爲如果其中一臺機器出現故障,其他機器可以作爲備份提供服務。

  3. 推文本身並不存儲在集羣中,而只存儲推文 ID。推文將在傳遞給用戶的同時被檢索出來。

  4. 如果用戶超過 30 天沒有登錄 Twitter,該用戶的時間線將不會保存在 Redis 集羣中。對於這類用戶,只有當他們返回並向主頁時間線發出請求時,纔會從磁盤重構他們的時間線。

每個用戶的主頁時間線上存儲的推文數量是有限制的,每次只向用戶顯示 800 條推文,這是爲了控制內存使用而做出的設計決策!

名人用戶的特殊處理💃🏻🕺🏻

上述優化適用於普通用戶,但名人有特殊的處理,下面我們以 Lady Gaga 爲例,她當時有大約 3100 萬粉絲。

當 Lady Gaga 發佈推文時,會發生 3100 萬次扇出操作!而且要複製 3 次!

結果就是,有些用戶可能會在 Lady Gaga 的推文發佈 5 分鐘後才能看到,因爲他們的時間線沒有被及時更新。

這就產生了一個不太好的效果,一個可以看到 Lady Gaga 推文的用戶會回覆這條推文,這條回覆將會在其他用戶的時間線中出現,而這些用戶還沒有收到 lady Gaga 的原始推文,也許可以稱之爲無頭推文(Headless tweets)🤔

爲了避免這種情況,Twitter 想出了一種混合方法:

  1. 如果一個人有太多的關注者,那麼就不對他們的推文做扇出操作。

  2. 這些用戶的推文只有在有人請求主頁時纔會被合併到時間線中。

又一個簡單的解決方案!

總結

研究 Twitter 的整個架構是非常困難的,尤其是現在已經成爲了大公司以後。

但從他們過去解決大規模問題的方式中學習,也可以幫助我們在工作的時候做出決策。即使在成千上萬的地方存儲一條推文聽起來很荒謬,如果能夠有效解決問題並且沒有任何副作用,這就一個很好的解決方案!

參考鏈接:

  1. https://www.infoq.com/presentations/Twitter-Timeline-Scalability/

  2. https://blog.twitter.com/engineering/en_us/topics/infrastructure/2017/the-infrastructure-behind-twitter-scale

  3. https://www.youtube.com/watch?v=6w6E_B55p0E

GoCN 最具規模和生命力的 Go 開發者社區

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/eUNuWvt8-5H_j1c1aC2ASQ