跳到主要内容

评论系统设计

问题

如何设计一个支持多级回复的评论系统?

答案

数据模型

评论表(二级评论方案)
CREATE TABLE comment (
id BIGINT PRIMARY KEY,
content TEXT,
user_id BIGINT,
target_id BIGINT, -- 评论目标(文章ID/视频ID)
target_type TINYINT, -- 目标类型
parent_id BIGINT DEFAULT 0, -- 0=根评论,非0=回复哪条评论
root_id BIGINT DEFAULT 0, -- 根评论ID(方便查同一楼的所有回复)
reply_to_uid BIGINT, -- 回复给谁
like_count INT DEFAULT 0,
status TINYINT DEFAULT 1,
created_at DATETIME,
INDEX idx_target (target_id, target_type, created_at),
INDEX idx_root (root_id, created_at)
);
二级评论方案

大多数平台(微博、B 站)采用二级评论:根评论 + 回复列表。不做无限嵌套,展示更清晰,查询更高效。

查询逻辑

分页获取评论(根评论 + 热门回复)
public CommentPageVO getComments(long targetId, int type, int page, int size) {
// 1. 查询根评论(按热度/时间排序)
List<Comment> rootComments = commentMapper.findRootComments(
targetId, type, (page - 1) * size, size
);

// 2. 批量查询每条根评论的前 3 条回复
List<Long> rootIds = rootComments.stream().map(Comment::getId).toList();
Map<Long, List<Comment>> repliesMap = commentMapper.findRepliesByRootIds(rootIds, 3)
.stream().collect(Collectors.groupingBy(Comment::getRootId));

// 3. 组装
List<CommentVO> list = rootComments.stream().map(root -> {
CommentVO vo = convert(root);
vo.setReplies(repliesMap.getOrDefault(root.getId(), List.of()));
return vo;
}).toList();

return new CommentPageVO(list, commentMapper.countRootComments(targetId, type));
}

点赞(Redis + 异步落库)

Redis Set 记录点赞关系
public boolean likeComment(long userId, long commentId) {
String key = "comment:likes:" + commentId;
Boolean added = redisTemplate.opsForSet().add(key, String.valueOf(userId)) > 0;
if (Boolean.TRUE.equals(added)) {
// 异步更新数据库计数
rocketMQTemplate.convertAndSend("COMMENT_LIKE",
new LikeEvent(userId, commentId, true));
}
return added;
}

常见面试问题

Q1: 为什么不用无限层级嵌套?

答案

  • 查询复杂,需要递归
  • 深层嵌套展示困难,用户体验差
  • 二级评论(根评论 + 回复列表)覆盖 99% 场景,微博、抖音都是这样

Q2: 评论排序怎么设计?

答案

  • 按时间:最新/最早
  • 按热度:点赞数 × 权重 + 回复数 × 权重 - 时间衰减
  • 按综合:先按热度排前几条,再按时间

Q3: 高并发点赞怎么处理?

答案

  • Redis Set 记录点赞关系,天然去重
  • 异步通过 MQ 批量更新数据库计数
  • 定时将 Redis 点赞数同步到 DB

相关链接