blog icon indicating copy to clipboard operation
blog copied to clipboard

数据库操作太多了

Open chunpu opened this issue 10 years ago • 0 comments

我发现才做了一点功能,数据库操作就多的不行了,比如访问一下功能正常的主页,要进行至少如下的数据库操作

  • token解析后找到登陆的人,1次
  • 显示最近发的帖子,如果一页30的话,1次
  • 每个帖子根据作者id,查询作者信息,1*30次
  • 如果有最近回复的id,那就是查询最近回复, 1*30次
  • 30个帖子中是否有收藏的,1*30次
  • 是否点赞,1*30次

也就是说大概要进行上百次的操作,这简直是可怕的,我查了下discus访问一下主页大概是要10-30次之间,这也很多了

很显然,肯定是我什么地方想错了,不该有这么多数据库查询

比如我一直在纠结一个帖子的评论使用mongoose的子文档还是就是放在正常的文档中呢

放在子文档会很爽,每次新增评论只需要向comments这个属性中push这个评论,只会有一次save操作

如果是单独的collection,则需要新增一个reply,并且更新last_reply_id,如果不更新last_reply_id,则会导致显示最近回复需要一次复杂的操作

数据库操作复杂起来,对于nodejs简直是致命的,因为每次回调加上错误判断,3次以上的异步操作,函数就斜的不能看了

所以是时候使用一下异步的库,以前确实不知道有专门解决回调过深的库,现在觉得真是丢死人,写js不用异步库简直智商拙计

我想了很久,认为model设计上出了问题

比如一个帖子,按照nodeclub的做法,在帖子的model中既有last_reply_id也有last_reply_at,其中last_reply_at似乎是必须的,因为大多数排序是按照最后回复时间来排序的

但这样也有问题

  • 如果还是要获取最后回复的用户的name,则还需加一层嵌套
  • 每次发布一个评论,都要在主题中加入last_reply_idlast_reply_at,有一个发生错误就会产生冲突
  • 删除评论,如果碰巧是最新的评论,还需要删除该主题的last_reply_idlast_reply_at并且重新找最新的回复,因此大部分都是直接写评论已删除。。

总之,光是这几点,就足以让人崩溃,我们理想中的方法就是添加一个评论,不牵扯其他,仅仅为了最新的回复排序要折腾这么多?

目前的想法就是在内存中保留一个hash,键为帖子的id,值为所有评论,按时间排序。同时保存一个数组,按时间排序hash的键

这样的话有如下好处

  • 按最新回复排序,只需从数组中取出值
  • 显示一个帖子的回复已经不需要查询
  • 新增一个帖子只需要加入数据库后,把返回值shift入该主题的评论数组,并重新排序时间数组
  • 删除一个评论只需要从数据库中删除,并将其从该主题的评论数组slice出来,并重新排序时间数组
  • 除了数据库操作外,其他都不是IO操作
  • 每次启动论坛程序,只需遍历全部主题,根据id查询评论,按时间输出

坏处

  • 把数据保存在变量中很容易出问题,比如一个帖子有1000个回复,有1w多个帖子了,但变量就至少是600M大,越到后面就越无法扩展

解决方法是获取全部主题的前100个,然后存在数组中

但这样实现想想就很蠢

我最终的决定是加上一个辅助的collections,专门用来统计,每次开启服务的时候取到回复数(注意浏览数不属于统计),收藏数,最近回复

这样,每添加一个回复,直接替换该帖子的最近回复即可,也就是说这个新增的collection是专门用于算出可以统计出来的信息,防止冲突

同时在api设计的时候,还在想如何把评论插件化,如果能顺利插件化,那以后做赞,收藏,踩这样的功能就会轻松许多

chunpu avatar Jan 10 '15 04:01 chunpu