feed服務項目設計思考

項目背景

當初出於留存的考慮,產品同事在app內設計了相似微博的feed功能。從功能上看,咱們的feed服務更像是微博和微信朋友圈的結合體。既有微博熱門的場景,也有微信朋友圈的影子。redis

功能列表

  • feed資料頁

相似微信朋友圈的相冊功能,能夠看到用戶曾經發布的feed動態。sql

  • feed新鮮事頁

相似微信朋友圈功能,能夠看到本身及好友(關注的人)發佈的feed動態。數據庫

  • feed廣場頁

相似微博的推薦或熱門功能,爲用戶作個性化的推薦。緩存

基本思路

feed流.png

項目思考

1. 數據存儲

在feed動態主要存儲兩種信息,一是動態內容,二是不一樣維度的動態索引。bash

feed數據結構

feed信息除了基本的內容外還須要存儲額外的信息,而且額外信息可能還會面臨擴展的狀況。因此feed數據結構基本定義以下。在數據庫裏面存儲的是FeedInfo信息序列化後的數據,這樣後續能夠支持擴展,而不須要修改數據庫字段。微信

message FeedItem {
    string feed_id = 1; //動態id
    int64 create_time = 2; //發佈時間
    User from_user = 3; //發佈者
    int32 feed_type = 4; //對應FeedType
    bytes attachment = 5; //附件信息
   ...
}

message FeedInfo {
    FeedItem feed_item = 1;
    bool deleted = 2; //是否刪除
    AuditType audit_type = 3; // 審覈類型
    VisibleStatus visible_status = 4; // 動態可見性
    ...
}
複製代碼

內容存儲

feedId => feed內容數據結構

動態內容能夠抽象成KV形式的鍵值存儲,幾乎全部的場景都是根據動態ID來獲取動態內容,而後進行後續處理。所以選用了HBase做爲優先的內容存儲服務,再以Mysql和Redis爲輔,做爲降級方案。畢竟是首次將HBase應用到在線服務。以最終的性能數據來看,HBase的性能仍是不錯的。app

索引數據存儲

其它維度的索引關係仍是使用Mysql存儲,畢竟可能會涉及到複雜的查詢。異步

  • 資料頁feed

相似微信朋友圈相冊功能,須要存儲用戶所發佈的全部動態,按用戶維度進行分表。基本信息以下:性能

屬性 備註
user_id 發佈者ID
feed_id 動態ID
feed_create_time 動態建立時間
visible_status 動態可見性
  • 新鮮事feed

相似微信朋友圈功能,須要存儲全部關注人本身的動態,按用戶維度分表。基本信息以下:

屬性 備註
user_id 用戶ID
feed_id 動態ID
from_user_id 發佈者ID
feed_create_time 動態建立時間
  • 廣場推薦feed

相似微博的熱門推薦功能,須要根據時間線存儲全部用戶發佈的feed。所以廣場feed根據日期進行分表。1個月有31天,這裏一共分紅31個表,不一樣日期的feed存儲到不一樣表。基本信息和新鮮事feed基本一致,只是存儲的數據及數據維度有所不一樣。

屬性 備註
user_id 用戶ID
feed_id 動態ID
from_user_id 發佈者ID
feed_create_time 動態建立時間

這裏按天分表有如下考慮:按當前預估發佈量,31個分表應該足夠,不須要再細分。按天分表基本能保證數據的連續性,比較符合查詢習慣。

2. 數據擴散

  • 擴散方式

數據擴散有寫擴散讀擴散兩種方式。 讀擴散是在讀取的時候再進行擴散,這樣一次讀請求可能會涉及好幾個地方的讀取,讀取耗時不可控。寫擴散通常是存儲多份數據,雖然冗餘存儲了不少數據信息,可是讀取的效率能夠提升很多,在當下磁盤等硬件資源並不緊缺的狀況下寫擴散應該是更爲合適的處理方式。

  • 數據流向

在feed服務裏面用戶發佈的feed會先在本人的資料頁及新鮮事頁可見,優先保證發佈者的體驗。而後發佈的feed纔會擴散到其粉絲好友和廣場頁進行曝光。後面的流程用戶基本對延遲不感知的,所以這裏經過消息隊列進行異步化的寫擴散處理。基本處理流程以下:

3.審覈機制

用戶發佈動態須要通過審覈,爲了不違規的內容推送給用戶。審覈機制通常有先審後發先發後審兩種。

  • 先審後發

用戶發佈的feed必須先經過審覈後才能夠擴散給其餘用戶。所以能夠在feed寫入發佈者資料頁和新鮮事頁以後,寫入消息隊列進行擴散以前進行攔截。只有當feed審覈經過後才寫入消息隊列進行寫擴散,這樣發佈者和其餘用戶也不會有明顯的感知。不過對於發佈者來講,可能會感受到互動的延遲。

  • 先發後審

用戶發佈的feed能夠先進行擴散曝光,當feed審覈不經過時才進行刪除操做。這種狀況下用戶的體驗會更好,不過會增長平臺的一些風險。

4.點贊設計

  • 用戶維度點贊列表

用戶點贊數據會存儲在數據庫和redis緩存。因爲用戶點贊行爲不肯定,部分用戶可能會頻繁點贊。所以使用redis的zset結構緩存用戶部分點贊數據,field爲feedId,score也是feedId,根據feedId倒序排列,只保留最新的N條feed點贊數據。

用戶點贊列表爲何不以點贊時間排序?由於用戶點贊時間是不肯定的,用戶頗有可能點讚了好久以前的feed。這樣就意味着當緩存裏沒找到feed點贊數據時,沒法肯定是緩存缺失仍是用戶沒點贊,最後都得在數據庫再次查詢作確認。

用戶點贊列表按feedId排序的狀況下,若feedId不在列表中,且feedId大於點贊緩存中最小的feedId,就能肯定用戶的確沒有點贊,不須要再從數據庫進行查詢。

  • feed維度點贊計數

在咱們的場景裏,feed維度點贊計數是重要的數據。一開始設計的時候,使用redis的count來同步計數,但是會存在漏計數或者重複計數的問題,沒辦法保證數據的準確性。後來考慮到點讚的頻率不會很高,調整成從數據庫獲取count計數,將計數再同步到redis緩存。

  • 拆分點贊流程

在設計裏面優先保證用戶端體驗,所以在存儲用戶維度的數據後會快速返回,將本次點贊請求寫入消息隊列。而後再處理feed維度點贊數據的維護,包括調整feed點贊計數和給feed發佈者發送點贊消息等。根據重要程度拆分流程,優先保證用戶的體驗。

5.trade off策略

  • 廣場推薦頁保底策略

廣場推薦feed通常是推薦服務根據用戶特徵返回其更感興趣的feed列表。當推薦服務不可用時,須要有個保底策略以確保用戶能正常拉取到feed信息,避免影響用戶體驗。這裏就實現了個保底策略,利用redis的zset結構維護全部用戶發佈的最新的N條feed。當推薦服務不可用時直接返回該列表的信息。

  • 被關注者最新的N條feed

當用戶新關注其餘用戶時,被關注者的feed應當出如今用戶的新鮮事頁面。考慮到信息的實時性,咱們只選擇被關注者最新的N條feed插入到用戶的新鮮事,而不是被關注者全部的feed列表。這樣處理基本也不影響用戶體驗,處理上相對簡單一點。

結語

這是第一次思考feed服務的設計並加以實現,基本涵蓋了主要的場景。在這過程當中也不斷地進行了一些小優化,也有不小的收穫,其中還有不少能夠再優化的地方。

相關文章
相關標籤/搜索