【系統設計】如何設計 Twitter 時間線和搜索?

如何設計 Twitter 時間線和搜索?

1.業務場景

業務場景如下:

  • 用戶發佈推文
    • 服務將推文推送給關注者,發送推送通知和電子郵件
  • 用戶查看用戶時間線(來自用戶的活動)
  • 用戶查看主頁時間線(用戶關注的人的活動)
  • 用戶搜索關鍵字
  • 服務具有高可用性

其他場景:

  • 服務將推文推送到 Twitter Firehose 和其他流
  • 服務根據用戶的可見性設置刪除推文
    • 如果用戶沒有關注被回覆的人,則隱藏回覆
    • 尊重“隱藏轉發”設置
  • 分析

2.業務要求

假設如下:

  • 流量分佈不均
  • 發佈推文應該很快
    • 向所有關注者發送推文應該很快,除非你有數百萬關注者
  • 1億活躍用戶
  • 每天 5 億條推文或每月 150 億條推文
    • 每條推文平均扇出 10 次交付
    • 每天通過扇出發送 50 億條推文
    • 每月通過扇出發送 1500 億條推文
  • 每月 2500 億次讀取請求
  • 每月 100 億次搜索

時間線

  • 查看時間線應該很快
  • Twitter 閱讀量大於寫入量
    • 優化推文的快速閱讀
  • 攝取推文寫得很重

搜索

  • 搜索應該很快
  • 搜索是重讀

簡單的對業務要求進行計算,轉換成業務指標

  • 每條推文的大小:
    • tweet_id- 8 個字節
    • user_id- 32 字節
    • text- 140 字節
    • media- 平均 10 KB
    • 總計:~10 KB
  • 每月 150 TB 的新推文內容
    • 每條推文 10 KB * 每天 5 億條推文 * 每月 30 天
    • 3 年內 5.4 PB 的新推文內容
  • 每秒 10 萬個讀取請求
    • 每月 2500 億次讀取請求 *(每秒 400 次請求 / 每月 10 億次請求)
  • 每秒 6,000 條推文
    • 每月 150 億條推文 *(每秒 400 條請求 / 每月 10 億條請求)
  • 每秒扇出 6 萬條推文
    • 每月通過扇出發送 1500 億條推文 *(每秒 400 個請求 / 每月 10 億個請求)
  • 每秒 4,000 個搜索請求
    • 每月 100 億次搜索 *(每秒 400 次請求 / 每月 10 億次請求)

方便的轉換指南:

  • 每月 250 萬秒
  • 每秒 1 個請求 = 每月 250 萬個請求
  • 每秒 40 個請求 = 每月 1 億個請求
  • 每秒 400 個請求 = 每月 10 億個請求

3.系統設計

1.系統設計

img

我們必須進行必要的服務拆分

  • Timeline Service : 時間線服務,獲取存儲在Memory Cache中的時間線數據,包含用戶ID和推文ID

    • TWeet Info Service: 推文信息服務,獲取有關推文ID的附加信息
    • User Info Service : 用戶信息服務,獲取有關UserID的附加信息
  • Fan Out Service:扇出服務,A發佈推文後,通知關注了A的所有用戶,A發了新推文

    • User Graph Service : 用戶關係服務,提供用戶之間的關係圖,比如A用戶關注了哪些用戶

    • Search Service : 關鍵字搜索服務,全文檢索(搜索集羣,Lucene)

    • Notification Service: 通知服務,向某用戶發送推文通知(你關注的用戶xx發了新推文)

2.用例實現

用例1:用戶發佈推文

我們可以將用戶自己的推文存儲在關係數據庫中以填充用戶時間線(來自用戶的活動)。

我們可以將照片和視頻等存儲在 Object Store

  • Client將推文發佈到Web Server,作爲反向代理運行
  • Web Server將請求轉發到Write API Server
  • Write API Server將推文存儲在SQL 數據庫上的用戶時間軸中
  • Write API Server 聯繫 Fan Out 服務,該服務執行以下操作:
    • 查詢 User Graph 服務,查找 內存緩存中存儲的用戶關注者
    • 將推文存儲在內存緩存中用戶關注者的主頁時間線中
      • O(n) 操作:1,000 個關注者 = 1,000 次查找和插入
    • 將推文存儲在Search Service中以實現快速搜索
    • Object Store中存儲媒體數據
    • 使用Notification Service 服務向關注者發送推送通知:
      • 使用隊列(未圖示)異步發送通知

內存緩存如果使用redis,可以使用如下結構的redis列表

           tweet n+2                   tweet n+1                   tweet n
| 8 bytes   8 bytes  1 byte | 8 bytes   8 bytes  1 byte | 8 bytes   8 bytes  1 byte |
| tweet_id  user_id  meta   | tweet_id  user_id  meta   | tweet_id  user_id  meta   |

新的推文也會被放在redis中,該緩存會填充用戶的主頁時間線(來自用戶關注人的活動)

$ curl -X POST --data '{ "user_id": "123", "auth_token": "ABC123", \
    "status": "hello world!", "media_ids": "ABC987" }' \
    https://twitter.com/api/v1/tweet

響應

{
    "created_at": "Wed Sep 05 00:37:15 +0000 2012",
    "status": "hello world!",
    "tweet_id": "987",
    "user_id": "123",
    ...
}

內部通信,可以用grpc

用例2:用戶查看主頁時間線

  • ClientWeb Server發佈主時間線請求
  • Web Server將請求轉發到Read API Server
  • Read API Server 與 Timeline Service聯繫,後者執行以下操作:
    • 獲取存儲在內存緩存中的時間線數據,包含推文 ID 和用戶 ID - O(1)
    • 使用multiget查詢Tweet Info Service以獲取有關推文 ID 的附加信息 - O(n)
    • 使用 multiget查詢User Info Service以獲取有關用戶 ID 的附加信息 - O(n)
$ curl https://twitter.com/api/v1/home_timeline?user_id=123

響應:

{
    "user_id": "456",
    "tweet_id": "123",
    "status": "foo"
},
{
    "user_id": "789",
    "tweet_id": "456",
    "status": "bar"
},
{
    "user_id": "789",
    "tweet_id": "579",
    "status": "baz"
},

用例3:用戶查看用戶自己的時間線

  • ClientWeb Server發佈用戶時間線請求
  • Web Server將請求轉發到Read API Server
  • Read API Server SQL 數據庫中檢索用戶時間線

類似於用例2的查看主頁時間線,除了所有推文都來自用戶自己而不是用戶關注的人。

用例4:用戶搜索關鍵字

  • ClientWeb Server發送搜索請求
  • Web Server將請求轉發到Search API Server
  • Search API Server 聯繫Search Service,它執行以下操作 :
    • 解析/標記輸入查詢,確定需要搜索的內容
      • 刪除標記
      • 將文本分解爲術語
      • 修正錯別字
      • 規範大寫
      • 將查詢轉換爲使用布爾運算
    • 查詢搜索集羣(即Lucene)以獲取結果:
      • Scatter 收集集羣中的每個服務器以確定是否有任何查詢結果
      • 合併、排名、排序並返回結果
$ curl https://twitter.com/api/v1/search?query=hello+world

除了與給定查詢匹配的推文外,響應將類似於主時間線的響應。

4.系統優化

img1

優化要點:

  • DNS
  • CDN
  • Load Balancer:負載均衡
  • SQL Read Relicas :讀多副本
  • SQL Write Master-Slave :寫主從模式

關於扇出服務的性能瓶頸:一個幾百萬的用戶A發推文,可能需要幾分鐘,才能通知到關注了A的用戶,A發送了新的推文:

當用戶A關注人數到達一定閾值的時候,可以讓Client主動搜我關注的A有沒有新發推文

其他優化:

  • 在內存緩存中只保留每個家庭時間線的數百條推文
  • 僅在內存緩存中保留活動用戶的主頁時間線信息
    • 如果用戶在過去 30 天內未處於活動狀態,我們可以從SQL 數據庫重建時間線
      • 查詢User Graph以確定用戶正在關注誰
      • 從SQL 數據庫中獲取推文並將它們添加到內存緩存中
  • Tweet Info Service中僅存儲一個月的推文
  • 僅在User Info Service中存儲活動用戶
  • 搜索集羣可能需要將推文保存在內存中以保持低延遲

參考:https://github.com/donnemartin/system-design-primer/blob/master/solutions/system_design/twitter/README.md

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