当前位置:首页 > 问答 > 正文

评论和回复数据库设计那些事儿,聊聊怎么搭结构更合理点

评论和回复功能,现在几乎是个网站或应用都得有,别看它好像就是用户打几个字点个提交,背后数据库怎么设计,可是直接影响着功能能不能顺畅跑起来,以后好不好维护,今天就来聊聊这事儿,怎么搭结构能更合理点。

最直接、最朴素的想法,可能就是建一张大表,就叫它“评论表”吧,里面放上评论ID、文章ID、用户ID、评论内容、评论时间这些基本字段,如果是一条直接对文章的评论,那么它的“父级ID”就设为0或者NULL,如果有人回复了某条评论,那么就把这条回复的“父级ID”设置成它回复的那条评论的ID,这种结构,通常被称为“邻接表”模型,它的好处是简单直观,一眼就能看明白评论和回复之间的父子关系,写起代码来,插入一条新评论或回复也非常简单,直接往表里插一行就行了。

评论和回复数据库设计那些事儿,聊聊怎么搭结构更合理点

这种简单是有代价的,当你想要展示一篇文章下面的所有评论和回复,并且要按照时间或者层级关系漂亮地排好序时,麻烦就来了,因为所有的评论和回复都混在一张表里,靠一个“父级ID”来关联,如果你想查询一条顶级评论下面的所有回复,数据库可能需要进行递归查询,或者你的应用程序需要多次查询数据库,先查出所有顶级评论,再为每一条顶级评论去查它的回复,当评论量大了,这种查询方式对数据库的压力会非常大,页面加载会变得很慢,这就是邻接表模型在查询上的主要缺陷:读取效率低,尤其是需要展示完整评论树的时候。

那有没有更好的办法呢?有,一种常见的改进方案是引入一个“路径”字段,有时候也叫“祖先路径”或“ lineage”,这个方案是在邻接表的基础上,增加一个字段,用来存储从最顶级的祖先到当前评论的整个ID路径,一条对文章的评论,它的ID是101,那么它的路径可能就是“,101,”,有人回复了101这条评论,新回复的ID是205,那么它的路径就是“,101,205,”,如果再有人回复205这条回复,ID是308,那么路径就是“,101,205,308,”。

评论和回复数据库设计那些事儿,聊聊怎么搭结构更合理点

这个方法妙在哪里呢?妙在查询的时候,当你想获取一篇文章下的所有评论,并且按照层级关系排序时,你不再需要复杂的递归查询了,你只需要一个简单的SQL语句,按照这个“路径”字段来排序就行了,因为路径字符串本身天然地包含了层级和顺序信息,路径短的肯定是顶级评论;路径长的则是更深层的回复,按字母序排序,自然就能得到“先顶级评论,然后每条顶级评论下面紧跟着它的所有回复”这样的顺序,这样一来,一次查询就能拿到所有数据,应用程序处理起来也方便很多,大大减轻了数据库的压力,这个方法是《SQL反模式》这本书里明确推荐用来解决树形结构查询问题的方案之一。

除了路径法,还有一种思路叫“闭包表”,这个听起来有点抽象,但其实理解后也很直观,它需要另外再创建一张表,专门用来记录节点(也就是评论)之间的关系,这张表通常只有两个字段:祖先ID和后代ID,对于每一条评论,它不仅是自己的后代,也是自己的祖先,评论ID为101的条目,在关系表中会有一条(101, 101)的记录,表示自己是自己的祖先和后代,如果205是101的回复,那么关系表里就会有多条记录:首先是(205,205)自己指向自己;然后是(101,205),表示101是205的祖先;注意,如果101还有祖先(比如101本身是对文章的评论,文章也算一个虚拟根节点),也需要记录,但实际上在评论系统中,我们通常只记录评论之间的直接关系。

评论和回复数据库设计那些事儿,聊聊怎么搭结构更合理点

闭包表的好处是查询任意节点的所有后代或者所有祖先都非常快,因为关系已经预先计算好并存储在表里了,但缺点也很明显,就是关系表的数据量会增长得很快,因为每增加一条评论,就需要向关系表插入多条记录,插入和删除评论时,需要同时维护评论表和关系表,逻辑上稍微复杂一些。

到底该选哪种呢?这得看你的具体场景,如果你的应用评论量不大,或者对性能要求没那么极致,从简单出发,用邻接表加上一些应用层的处理,可能也够用了,如果你的应用评论互动非常活跃,非常看重评论列表的加载速度,那么增加一个路径字段会是性价比非常高的选择,它在查询效率上提升显著,同时维护成本增加不多,而闭包表,则更适合那种需要频繁进行复杂层级关系分析的场景,对于一般的文章评论系统来说,可能有点杀鸡用牛刀了。

在设计时还要考虑一些扩展性问题,如何支持对评论的点赞、点踩?可以增加计数字段,但要注意并发更新的问题,评论是否允许引用或回复特定的某条回复?这可能需要额外的字段来标记“回复目标ID”,评论是否需要审核状态?软删除如何实现?这些都需要在表结构里预留出字段或者做好规划。

评论数据库设计没有唯一的“标准答案”,核心是在读写性能、复杂度和业务需求之间找到一个平衡点,从简单的邻接表开始,随着业务增长,再考虑引入路径法等优化方案,是一个务实且稳妥的演进路线,希望这些实实在在的讨论,能对你搭一个更合理的评论结构有所帮助。