面试官: 如何设计一个评论系统?

在内容类产品中,评论系统几乎是标配。无论是博客、社区、视频平台,评论都是提升互动的关键组成部分。然而,评论系统的设计并非"一套走天下",它的底层结构需要根据业务的流量体量灵活调整。

本篇文章将结合不同流量场景,从表结构设计、查询逻辑、技术选型三个维度,讨论评论功能在不同阶段的落地方案,让你轻松应对面试!


场景一:中小流量系统(单库单表 + 两级评论)

适用场景:CSDN、掘金这类文字内容为主、日活低于 10 万的站点。

表结构设计

采用单表结构支持两级评论(一级评论 + 回复),结构简单、易维护:

sql 复制代码
CREATE TABLE comment (
  id BIGINT PRIMARY KEY,
  biz_id BIGINT NOT NULL,           -- 文章、视频等业务 ID
  biz_type TINYINT NOT NULL,        -- 枚举:1=文章 2=视频
  user_id BIGINT NOT NULL,          -- 评论人
  parent_id BIGINT DEFAULT 0,       -- 父评论 ID,0 表示一级评论
  root_id BIGINT DEFAULT 0,         -- 根评论 ID,自己是一级时等于 id
  content TEXT NOT NULL,
  like_count INT DEFAULT 0,
  reply_count INT DEFAULT 0,
  status TINYINT DEFAULT 0,         -- 0=正常 1=审核中 2=已删除
  created_at DATETIME,
  INDEX idx_biz (biz_id, parent_id, id),
  INDEX idx_root (root_id),
  INDEX idx_user (user_id)
);

查询逻辑设计

查询某个业务对象的一级评论(首屏 / 下拉翻页)

sql 复制代码
SELECT * FROM comment
WHERE biz_id = #{bizId} AND parent_id = 0 AND status = 0
ORDER BY id DESC
LIMIT #{pageSize} OFFSET #{offset};

查询某条评论的回复

sql 复制代码
SELECT * FROM comment
WHERE root_id = #{commentId} AND parent_id != 0 AND status = 0
ORDER BY id ASC;

示例代码

java 复制代码
public List<Comment> getTopComments(Long bizId, int page, int size) {
    return commentMapper.selectList(
        new LambdaQueryWrapper<Comment>()
            .eq(Comment::getBizId, bizId)
            .eq(Comment::getParentId, 0)
            .eq(Comment::getStatus, 0)
            .orderByDesc(Comment::getId)
            .last("LIMIT " + size + " OFFSET " + (page - 1) * size)
    );
}

public List<Comment> getReplies(Long rootId) {
    return commentMapper.selectList(
        new LambdaQueryWrapper<Comment>()
            .eq(Comment::getRootId, rootId)
            .ne(Comment::getParentId, 0)
            .eq(Comment::getStatus, 0)
            .orderByAsc(Comment::getId)
    );
}

场景二:中高流量系统(分库分表 + 异步更新)

适用场景:知乎、B 站、头条等中大型社区,评论量过亿,日均写入上百万。

架构策略

项目 策略
分表方式 按 biz_id 哈希路由,例如 comment_0 ~ comment_63
ID 生成 雪花算法(Snowflake)
点赞计数 Redis + MQ 异步聚合写库
审核系统 评论先落审核队列,审核通过再入主表
热度排序 预计算 hot_score,或 Redis ZSET 排序

表结构(逻辑结构)

sql 复制代码
comment_${n} (
  id BIGINT PRIMARY KEY,
  biz_id BIGINT,
  user_id BIGINT,
  parent_id BIGINT,
  root_id BIGINT,
  content TEXT,
  like_count INT,
  reply_count INT,
  hot_score DOUBLE,
  status TINYINT,
  created_at DATETIME,
  INDEX(biz_id, parent_id),
  INDEX(root_id)
)

点赞、回复计数等异步写入:

java 复制代码
// 点赞后异步写 MQ
likeService.like(commentId, userId);
mqProducer.send("COMMENT_LIKE", commentId);

查询某个视频的所有一级评论(分页 + 热度)

sql 复制代码
SELECT * FROM comment_03
WHERE biz_id = 1001 AND parent_id = 0 AND status = 0
ORDER BY hot_score DESC
LIMIT 20;

热度分数一般为:

ini 复制代码
hot_score = like_count * 0.8 + reply_count * 1.0 + time_decay(create_time)

热度排序 往往是分数衰减函数: hot_score = like_count * 0.8 + reply_count * 1.0 + create_ts / decay 在 MySQL 里做会很慢,B 站/知乎大多把热排算分移到 Redis ZSET 或 ES 脚本排序。

技术栈建议

  • 分库分表:ShardingSphere 或 MyCat
  • 缓存层:Redis
  • MQ:RocketMQ / Kafka
  • 审核:阿里绿网 / 自建敏感词 + 模型系统

场景三:超大流量系统(KV 存储 + 时间线索引)

适用场景:抖音、快手、Instagram 等亿级日活的移动平台

架构模型

评论内容存储在 TiKV、HBase、DynamoDB 等 KV 型数据库中;列表分页使用 Redis Timeline(ZSet/List)索引。

  • 写入通道 :先落 Kafka,流式计算(Flink / Spark Streaming)做审核与反垃圾,最后异步写入 HBase / TiKV

  • 读取通道:双层时间线(Timeline)模型

    • Root TL:视频 ID → 顶级评论的 ID 列表(ZSET or List,内存保 1 万条,冷数据分页补齐到 HBase)
    • Reply TL:顶级评论 ID → 子评论 ID 列表
    • 接口按「cursor = last_id」方式增量拉取;天然支持滚动上拉。

数据结构设计

text 复制代码
# 存评论
HBase: comment:{commentId} → 评论内容结构

# 根评论时间线
Redis: tl:root:{videoId} = ZSET(commentId, hot_score)

# 回复评论时间线
Redis: tl:reply:{rootId} = List(commentId1, commentId2,...)

查询流程(伪代码)

java 复制代码
List<Long> topIds = redis.zrevrange("tl:root:video123", offset, offset + 19);
Map<Long, Comment> commentMap = hbase.batchGet("comment", topIds);

点赞系统优化

  • 用户点赞写入 Redis Set(like:{userId}),前端展示批量判断
  • 定时汇总写入 ClickHouse 或 MySQL
  • 热门评论 like_count 每分钟同步

优化建议

问题场景 优化方式
评论首屏加载慢 Redis 缓存首屏 JSON,MQ 同步更新
点赞/回复不一致 所有修改走 MQ 异步聚合写库
"热评"插入后楼层错乱 楼层号稀疏生成,避免重排
评论审核敏感 写入审核表,通过后再搬迁
分页 OFFSET 性能差 id < last_id 做滚动翻页

总结

流量规模 技术方案 查询方式
中小体量 单表 + 两级 root_id MySQL 直接分页 + join
中高体量 分表 + root_id + 异步聚合 分表查询 + Redis 缓存
超大规模 HBase/TiKV + Redis Timeline 多层缓存 + KV 查询

抛开业务谈设计都是耍流氓,根据不同的规模选择最合适的方案,满足现有业务发展需求,便是优秀的架构设计。 小体量可采用通用设计模型,预留关键扩展点(如 ID 算法、root_id、MQ 链路),未来才能轻松应对流量爬坡。

最后

如果文章对你有帮助,点个免费的赞鼓励一下吧!关注公众号:加瓦点灯, 每天推送干货知识!

相关推荐
陈随易23 分钟前
VSCode v1.103发布,AI编程任务列表,可用GPT 5和Claude 4.1
前端·后端·程序员
中等生26 分钟前
Python的隐形枷锁:GIL如何"绑架"了你的多线程梦想
后端·python
Pitayafruit1 小时前
【📕分布式锁通关指南 12】源码剖析redisson如何利用Redis数据结构实现Semaphore和CountDownLatch
redis·分布式·后端
哈基米喜欢哈哈哈1 小时前
Netty入门(二)——网络传输
java·开发语言·网络·后端
尘心不灭1 小时前
Spring Boot 项目代码笔记
spring boot·笔记·后端
小高0071 小时前
GPT-5震撼登场!从单一模型到协作系统,AI架构的革命性突破
前端·后端·chatgpt
不老刘1 小时前
AI助力前端开发:Claude生成Element UI + TinyMCE集成135编辑器的代码
后端
coding随想1 小时前
深入浅出数据库事务:原子性、一致性、隔离性、持久性
后端
我是哪吒1 小时前
分布式微服务系统架构第160集:百万台设备Netty网关架构
后端·面试·github
DBdoctor1 小时前
批量删除大户!定时任务引发MySQL数据库IO负载飙升案例分析
后端