如何設計評論系統?

實際上,90%的app採用簡單的評論設計就能夠了,也就是採用一問一答,相似於以下的設計。mysql


這種設計十分簡單、直接,也知足了用戶評論、回覆的基本要求,對於沒有大量用戶評論或者評論不是核心功能的app來講就夠用了。暫且把這種場景稱之爲場景Asql

若是你是新聞類或者諮詢類的app,有着大量的用戶評論,那麼設計「蓋樓」的效果仍是可取的,這樣能幫助用戶找到該條評論或者回復的上下文情景。可是根據「蓋樓」的顯示效果不一樣,設計上也是有很大的差異的。若是是以評論爲主的顯示方式,相似於下面的顯示方式。數據庫


這裏能夠把評論分爲評論回覆,全部的回覆均掛在評論下面,相似於樹狀結構。把這種場景稱之爲場景Bjson

最後就是相似於網易新聞的評論設計了,貼一張截圖後端


這種場景下設計最爲複雜,由於回覆和評論是同等級的,回覆還能夠引用完整的回覆路徑,就是能夠溯源到最開始的評論上。這種場景我將至稱爲場景C緩存


數據庫設計

因爲我 一直使用mysql,我就以mysql爲例談一下針對上面三種場景的設計。微信

場景A

這種場景下通常評論數量較少,評論不爲活躍,能夠把不區分評論和回覆,而統一當作評論。區別在於有的評論是直接評論主題(每一個評論都掛在某個主題下,如文章、帖子等),而有些評論是@其餘用戶的,爲了能cover這兩張場景,使用一張表就能夠達到效果,評論表以下設計:數據結構

表字段 字段說明
id 主鍵
topic_id 主題ID
topic_type 主題type
content 評論內容
from_uid 評論用戶id
to_uid 評論目標用戶id

爲了能複用評論模塊,咱們引入一個topic_type字段來區分主題的類別。閉包

from_uid表示評論人的id,經過該id咱們能夠檢索到評論人的相關信息。架構

to_uid 是評論目標人的id,若是沒有目標人,則該字段爲空。

出於性能的考慮,每每咱們會冗餘評人的相關信息到評論表中,好比評論人的nick、頭像等,目標用戶也是如此。這樣一來咱們就只用查詢單表就能夠達到顯示的效果。

有時,目標用戶有多個,那麼能夠將to_uid字段修改成to_uids,保存時用分隔符來分割用戶id,而目標用戶的信息再去查詢緩存或者數據庫。也能夠簡單的將多個目標用戶的信息一塊兒存成json格式,能夠應付簡單的展示需求。

場景B

在以評論爲主的樹形顯示的狀況下,數據庫的設計十分靈活,可使用單表,添加一個parent_id字段來指向父評論。若是數據庫自己支持嵌套查詢,那麼仍是比較方便的,SqlServer、Oracle都支持,可是mysql不支持,那就只能經過存儲過程來實現。在互聯網應用中,能不使用觸發器 `存儲過程`的話,儘可能不要去使用,由於其對性能有影響。

咱們還能夠將評論拆分爲評論表回覆表評論掛在各類主題下面,而回覆都掛在評論下面。

評論表的設計以下:

表字段 字段說明
id 主鍵
topic_id 主題ID
topic_type 主題type
content 評論內容
from_uid 評論用戶id

回覆表的設計以下

表字段 字段說明
id 主鍵
comment_id 評論ID
reply_id 回覆目標id
reply_type 回覆類型
content 回覆內容
from_uid 回覆用戶id
to_uid 目標用戶id

因爲咱們拆分了評論和回覆,那麼評論表就再也不須要目標用戶字段了,由於評論均是用戶對主題的評論,評論表的設計更佳簡潔了。

回覆表我添加了一個comment_id字段來表示該回復掛在的根評論id,這樣設計也是出於性能方面的考慮,咱們能夠直接經過評論id一次性的撈出該評論下的全部回覆,而後經過程序來編排回覆的顯示結構。經過適當的冗餘來提升性能也是經常使用的優化手段之一。

reply_type表示回覆的類型,由於回覆能夠是針對評論的回覆(comment),也能夠是針對回覆的回覆(reply), 經過這個字段來區分兩種情景。

reply_id表示回覆目標的id,若是reply_type是comment的話,那麼reply_id=commit_id,若是reply_type是reply的話,這表示這條回覆的父回覆。

在數據結構的設計上,我在replyDTO中設計了一個List<ReplyDTO> next屬性,這樣在造成了一個樹形的結構,相似以下結構。


樹形結構

客戶端能夠直接根據該結構來進行樹形結構的顯示。

場景c

要達到網易新聞中評論的效果我尚未特別好的建議。這種場景中評論和回覆是同級顯示的,回覆不在顯示結構上不用掛在一個評論下面。雙表的設計在這裏就不太合適了,由於涉及到評論和回覆的混排,使用雙表則會致使查詢的邏輯過於複雜。因此建議仍是採用單表的設計,不區分評論和回覆會簡化應用層的邏輯。咱們統一都當作評論,而有些評論是能夠引用其餘評論的。本人推薦採用閉包表的設計,例如:

comment表設計

表字段 字段說明
id 主鍵
topic_id 主題ID
topic_type 主題type
content 評論內容
from_uid 評論用戶id

parent_children表

表字段 字段說明
id 主鍵
parent_id 父id
child_id 子id

comment表保存全部評論內容,而parent_children表則記錄評論表中各個評論的父子關係。

查詢時每每會按照時間排序,咱們能夠直接按id或者建立時間降序排列查詢comment表便可。若是用戶想查詢一條評論的完整引用,則能夠經過parent_children來找到對應的路徑。向上查找到評論只須要可執行:

select parent_id from parent_children where child_id=${id} and parent_id != ${id}

向下查找全部的子孫評論可執行:

select child_id from parent_children where parent_id = ${id} and parent_id != ${id}

閉包表在查詢時很是方便,可是插入的性能稍差,由於除了插入評論表之外,還須要把該條評論全部的父子關係插入到父子關係表中。插入性能會隨着評論層級的加深而線性降低。

海量數據優化

若是你的系統天天都會有成千上萬條評論,那麼單表的設計確定是不行,優化的方式也有不少。

  • 分庫分表。分庫分表是最爲經常使用也最有效的優化方式,建議按照主題來分庫分表。這樣同一個主題下面的評論就會落到同一張表裏,避免了跨表查詢。

  • 適當的數據冗餘。若是你須要顯示評論人的相關信息,那麼在插入評論時就把這些信息寫入評論表中,避免屢次查詢。實際上,若是是紀錄數據,均可以冗餘對應的數據信息,由於它們的數據的實時行和一致性要求並不高,用戶不會由於評論中的頭像沒更新而撕了你,哈哈。

  • 附加冪等數據只容許單項操做。若是pd要求你能給評論點贊,那麼你能夠告訴他只能點贊,不能取消。由於從冪等性的要求來講,每一個贊都是一條記錄。評論的贊數若是都從點贊表中統計得出,那麼性能開銷會十分巨大,並且點贊如此輕量級的一個操做必定會加重點贊表的競爭操做。因此建議直接在評論表中添加一個like_count的計數器,該字段只增不減。

  • 熱門評論加緩存。相似於網易新聞的熱門評論,讀取頻度很是高,能夠專門開接口給客戶端,同時該接口作緩存。




Java後端架構,關注獲取更多技術分享


歡迎加入互聯網後端架構

聯繫QQ:517894513

QQ羣號碼:217023887



本文分享自微信公衆號 - 互聯網後端架構(fullstack888)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索