评论系统设计思路

news/2024/11/8 15:07:39 标签: java, 前端, 数据库

文章目录

  • 一 表设计
      • Articles(文章表)
      • Comments(评论索引表)
      • CommentsContent(评论内容表)
      • SQL 创建表的语句
      • 触发器
  • 二 添加评论
  • 三 查询评论

无论我们是阅读公众号文章还是刷短视频,现在都有评论功能,而且这些评论基本上也都是支持“楼中楼”,也就是文章下面有评论,评论下面有回复,回复下面又有回复,回复还可以继续回复…

如果数据量不大的话,这个表其实很好设计,一张表就可以搞定,类似我们常见的省市县表结构:

CREATE TABLE administrative_divisions (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  parent_id INT DEFAULT NULL,
  level TINYINT NOT NULL,
  country_code VARCHAR(10),
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  INDEX idx_parent_id (parent_id),
  INDEX idx_level (level)
);

对于这种结构的表,数据量不大的话没问题,数据量大的话,查询性能和维护都会成为问题。

今天我们就来讨论下这种评论表如何设计会好一些,也是松哥最近工作中的一点点经验,欢迎小伙伴们批评指正。

一 表设计

因为现在无论是文章还是短视频,评论数动辄很大,所以系统在显示的时候,往往会先进行评论折叠,只显示一个总数,点击展开的时候,才会显示出来,以微信公众号为例,一般是这样:

首先在文章下方会先展示一个评论总数:

点开评论区之后,这里也有两个值得关注的信息:

  1. 评论区上方会显示总的评论数
  2. 回复的评论不会一次性显示出来,但是会显示有多少条回复,需要用户点击之后才可以看到回复内容。

另外需要注意的是,如果回复数量特别多的话,也不会一次性全部展示出来。

首先这样的设计是合理的,这个相信大家应该没什么异议,评论回复之所以不一次性全部展示,一方面是性能考虑,另一方面也是用户体验考虑。有的评论回复特别多,一次性全部展示出来,会影响查看一级评论。

那么我们就看下这样的评论功能,表该如何设计。

为了满足这些要求,我们将设计三个表:Articles(文章表),Comments(评论索引表),和 CommentsContent(评论内容表)。文章表将记录文章的根评论数,评论索引表将记录评论的层级关系、回复数量和点赞数量,评论内容表将存储实际的评论内容。

这种设计方式通过将评论内容和评论索引分开存储,提高了查询性能,同时也便于数据维护,并且通过文章表记录文章的根评论数,方便快速统计。

以下设计主要提供思路,大家不必纠结于表名称。

Articles(文章表)

字段名数据类型描述
article_idBIGINT AUTO_INCREMENT主键,自增
titleVARCHAR(255)文章标题
root_comment_countINT DEFAULT 0文章的根评论数
created_atDATETIME创建时间
updated_atDATETIME更新时间

Comments(评论索引表)

字段名数据类型描述
comment_idBIGINT AUTO_INCREMENT主键,自增
article_idBIGINT文章ID
parent_idBIGINT DEFAULT 0父评论ID,用于实现多级评论,顶级评论此字段为0
user_idBIGINT发表评论的用户ID
reply_countINT DEFAULT 0这条评论的回复数量
like_countINT DEFAULT 0这条评论的点赞数量
created_atDATETIME创建时间
updated_atDATETIME更新时间

CommentsContent(评论内容表)

字段名数据类型描述
comment_idBIGINT外键,关联评论索引表的comment_id
comment_textTEXT评论内容
comment_timeDATETIME评论时间

SQL 创建表的语句

-- 创建文章表
CREATE TABLE articles (
  article_id BIGINT AUTO_INCREMENT PRIMARY KEY,
  title VARCHAR(255) NOT NULL,
  root_comment_count INT DEFAULT 0,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  INDEX idx_root_comment_count (root_comment_count)
);

-- 创建评论索引表
CREATE TABLE comments (
  comment_id BIGINT AUTO_INCREMENT PRIMARY KEY,
  article_id BIGINT NOT NULL,
  parent_id BIGINT DEFAULT 0,
  user_id BIGINT NOT NULL,
  reply_count INT DEFAULT 0,
  like_count INT DEFAULT 0,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  INDEX idx_article_id (article_id),
  INDEX idx_parent_id (parent_id)
);

-- 创建评论内容表
CREATE TABLE comments_content (
  comment_id BIGINT PRIMARY KEY,
  comment_text TEXT NOT NULL,
  comment_time DATETIME DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (comment_id) REFERENCES comments(comment_id)
);

触发器

触发器主要用于更新文章的根评论数和评论的回复数量及点赞数量,不过这个并非必须,这个也可以通过业务逻辑去处理,松哥这里主要是给大家提供一个参考。

DELIMITER //
CREATE TRIGGER after_comment_insert
AFTER INSERT ON comments
FOR EACH ROW
BEGIN
  IF NEW.parent_id = 0 THEN
    UPDATE articles SET root_comment_count = root_comment_count + 1 WHERE article_id = NEW.article_id;
  END IF;
END; //
DELIMITER ;

CREATE TRIGGER after_comment_like_insert
AFTER INSERT ON comments
FOR EACH ROW
BEGIN
  UPDATE comments SET like_count = like_count + 1 WHERE comment_id = NEW.comment_id;
END; //
DELIMITER ;

CREATE TRIGGER after_comment_reply_insert
AFTER INSERT ON comments
FOR EACH ROW
BEGIN
  UPDATE comments SET reply_count = reply_count + 1 WHERE comment_id = NEW.parent_id;
END; //
DELIMITER ;

CREATE TRIGGER after_comment_delete
BEFORE DELETE ON comments
FOR EACH ROW
BEGIN
  IF OLD.parent_id = 0 THEN
    UPDATE articles SET root_comment_count = root_comment_count - 1 WHERE article_id = OLD.article_id;
  ELSE
    UPDATE comments SET reply_count = reply_count - 1 WHERE comment_id = OLD.parent_id;
  END IF;
END; //
DELIMITER ;

Comments 表的 article_idparent_id 字段上创建索引,可以提高查询特定文章的评论及其子评论的性能。

根据上面的设计,当我们想要获取评论总数、回复总数的时候,直接查询就可以了,就不需要统计了,非常方便。

二 添加评论

为了应对高并发,添加评论这块我们可以结合 MQ 去完成。

  1. 首先前端发送评论到服务端。
  2. 服务端收到评论消息之后,将之发给 MQ 去处理。
  3. 消费者从 MQ 上消费消息,向数据添加评论。
  4. 添加完成后,将添加成功的消息发送给客户端。

以上步骤需要确保 MQ 消息可靠性,具体如何确保可靠性,松哥在之前的文章中和大家聊过了(四种策略确保 RabbitMQ 消息发送可靠性!你用哪种?
),这里就不再赘述。

另外还有一点就是如果需要对评论内容进行分析,那么在第 4 步完成之后,还是通过消费 MQ 消息将评论存入 ES;或者通过 Canal 之类的工具,将表中的数据同步到 ES。

三 查询评论

热门评论可以存入缓存中,避免每次查询数据库,这个松哥之前有过专门的文章,小伙伴们可以参考:

  • 点赞收藏功能该如何设计?

虽然当时写的是点赞收藏,但是和这里思路基本一致。

另一方面就是读写分离。

评论数据是非常典型的读多写少场景,因此我们可以从数据库层面进行读写分离,以提升性能。

好啦,目前的思路大致上就是这样,如果有不同看法,欢迎小伙伴们留言讨论。


http://www.niftyadmin.cn/n/5744051.html

相关文章

【Apache ECharts】<病虫害致粮食损失统计>

实现 1. 设置 div &#xff08;块级盒子&#xff09;,设置 id 为 chart <div id"chart"></div> 2. css设置样式位置 <style>#main{width: 30%;height: 40vh;/* background-color: red; */min-height: 100px;min-width: 150px;margin-top: 150px;}…

【算法与数据结构】【链表篇】【题1-题5】

题1.从尾到头打印链表 题目&#xff1a;输入一个链表的头结点&#xff0c;从尾到头反过来打印出每个节点的值。链表的定义如下&#xff1a; struct ListNode {int mValue;ListNode *mNext;ListNode *mPrev; }; 1.1 方法一&#xff1a;栈 思路&#xff1a;要反过来打印&…

聊一聊SpringBoot中的自定义Starter

前言 自己动手简单的封装、应用一个starter 该starter的作用是被引入后&#xff0c;会对项目中Controller出现的异常做统一的处理及反馈 starter的思想在实际开发过程被大量的应用 一、新建starter项目 使用IDE创建一个maven构建方式的空白项目 1.1pom.xml <?xml vers…

独立站 API 接口的性能优化策略

一、缓存策略* 数据缓存机制 内存缓存&#xff1a;利用内存缓存系统&#xff08;如 Redis 或 Memcached&#xff09;来存储频繁访问的数据。例如&#xff0c;对于商品信息 API&#xff0c;如果某些热门商品的详情&#xff08;如价格、库存、基本描述等&#xff09;被大量请求…

Java后台生成指定路径下创建指定名称的文件

1.Java后台生成指定路径下创建指定名称的CSV文件 /*** <生成csv文件>* param filePath 文件路径名称* param fileName 文件名称* param colNameList 标题数据信息* param dataList CSV的文件数据* return filePathfileName* throws*/public static File genera…

【AIGC】如何通过ChatGPT轻松制作个性化GPTs应用

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | GPTs应用实例 文章目录 &#x1f4af;前言&#x1f4af;什么是GPTsGPTs的工作原理GPTs的优势GPTs的应用前景总结 &#x1f4af;创建GPTS应用的基本流程进入GPTs创建界面方式一&#xff1a;按照引导完成生成创建GPTs方式二…

AI时代,通才可能会占据更有利的地位

在AI时代&#xff0c;通才不仅有生存的可能&#xff0c;而且根据多个参考内容&#xff0c;他们实际上可能占据更有利的地位。以下几点解释了为什么通才在人工智能时代具有重要性和生存空间&#xff1a; 适应性和灵活性&#xff1a;通才因其广泛的知识基础和跨领域的技能&#x…

爬虫学习6

JSON&#xff08;JavaScript Object Notation&#xff09;和JSONP&#xff08;JSON with Padding&#xff09;是两个不同的概念&#xff0c;它们的主要区别在于用途和实现方式&#xff1a; ### JSON&#xff08;JavaScript Object Notation&#xff09; 1. **定义&#xff1a…