原创 龙虾 龙虾编程 2024年10月09日 08:01 浙江
文章或者视频一般都有评论模块,因为评论不仅可以增强用户的参与度、促进交流,而且通过用户的各种精彩评论与回复增加了内容吸引力,所以文章或者视频下的评论是非常重要的一个板块,今天我们来聊聊评论模块的设计。
1、基础设计
如下是一个文章的评论截图:
通过上图的评论图我们发现用户发表了一级评论之后,其他的用户可以在这个一级评论上继续评论成为二级评论,多个二级评论不用一次性展示,只展示二级评论的数量。
用户可以继续在二级评论的基础上再次评论成为三级评论,如果多个三级评论只展示三级评论的数量,如下如所示:
针对上图的评论图我们设计数据表字段。在评论中针对不同的主题(文章或者视频)我们有一个主题表,主题表主要的字段如下设计:
主题表为了做到通用,我们设计type来区分不同的内容来源;主题表中设计一个count字段主要的意义在于一个精彩的内容会有多个用户对这个主题进行评论,为了展示评论总数量,我们设计了此字段,这样就不用在查询所有的评论总数量,如下所示的效果图:
如果对某个一级评论的二级评论数量过多,此时只展示剩余二级评论的数量,当用户点击这个数字之后我们再去查询具体的评论信息。
因为评论内容可能会很多,所以我们考虑将内容和索引表分开,目的是减少mysql一次查询所占用的内存开销。设计的索引表和内容表如下所示:
数据表设计出来之后,我们如何支持到楼中楼(一级评论下有二级评论,二级评论下有三级评论),如何支持多个下级评论只展示数量呢?如下的评论表的关联关系图:
(a)root = 0表示当前是一个根评论(一级评论),当前的一级评论存在二级,一级评论的id作为二级评论的parentId和root,同样道理,二级评论下的三级评论一样的方式来关联。每级评论上都要记录下级评论的数量(就是count字段的值)目的是用来给前端展示剩余的下级评论数量。
(b)root > 0 表示的当前为非根评论(一级评论),通过root我们可以找到这个评论归属于哪个根评论。
上面分析楼中楼评论的存储方案,现在如何读取评论数据呢?如下图所所以的一个简单的多级评论图:
一级评论(root=0)的id关联下属的所有下级评论,我们通过一级评论的id关联二级评论的parentId,这样我们就可以查询到所有的一级评论下的二级评论,同样的二级评论的id作为三级评论的parentId,这样就可以找到二级评论下的所有三级评论。这样针对复杂的楼中楼评论我们就可以设计出来了。
2、保存评论
(1)用户通过客户端发出写入评论的请求,请求通过nignx和网关层转发到评论服务上,为了提高评论服务响应速度和支持高并发,我们采用异步的MQ的方式写入评论,也就是请求到了评论服务之后,评论服务发送一条评论写入的MQ消息。
(2)消息服务开启多线程消费MQ消息,将评论的数据写入到数据库中。
(3)消费完毕之后采用服务端主动推送写入成功的评论到客户端展示。
(4)评论写入数据库成功之后,我们通过canal的方式将数据同步到es中,这样运营人员可以通过es查询和管理评论。
3、高并发下评论系统的优化
在高并发下,评论系统的需要做优化的应对大流量的冲击,如下的一些常见手段:
(1)缓存热点评论
通过定时任务定期从数据库中提取最受欢迎的热点评论,然后加载到Redis中。这样用户在访问这些评论时,系统可以直接从缓存中读取,而不必每次都去查询数据库。同时为了避免缓存中的数据长期不更新,我们可以设置缓存的失效时间,缓存中的评论过期后重新加载新的热点评论。
(2)评论数据读写分离
读写分离适用于读操作远多于写操作的场景。在评论系统中,读操作远比写操作频繁,所以通过读写分离来提升系统的读取性能。
普通评论的读取操作分配到从库中,避免主库负担过重,同时结合热点评论缓存的方式,可以有效地减少数据库的读取压力,提高系统的响应速度。
(3)评论异步写入
评论数据不直接写入到数据路中,而是采用异步MQ的方式写入到数据库中,这样可以大大的提高系统的响应速度。