使用基于MongoDB的应用程序计算post计数器的正确方法是什么?

时间:2016-05-16 20:10:11

标签: node.js mongodb mongoose

我正在建立论坛,提出像SO这样的问题和答案。我的数据库是MongoDB,我的问题是如何正确存储和计算与post相关的计数器?

例如,我有两个集合:

文章:

[{
   _id: ObjectId(1),
   title: 'Untitled',
   content: 'empty'
}]

投票:

[{ 
    _post: ObjectId(1),
    _user: ObjectId(...),
    vote: 1,        
}, {
    _post: ObjectId(1),
    _user: ObjectId(...),
    vote: 1,
}]

当用户投票使用ObjectId(1)发帖时,他会发出以下查询:

Votes.create({_post: ObjectId(1), vote: 1}).exec(cb);

所有看起来都很好但是当我需要获取最后100个帖子的大块时,我会遇到性能问题。我需要的是:

  1. 查询帖子块
  2. 查询所有帖子的所有投票并手动合并。
  3. 我想修复性能问题,并在Post schema附加字段中计算投票次数:

    [{
       _id: ObjectId(1),
       title: 'Untitled',
       content: 'empty',
       votes: 2
    }]
    

    但在这种情况下,我注意以某种方式支持一致性和原子更新在Votes集合中创建新投票时投票。

    您能否建议最佳做法来支持帖子和投票集合之间的一致性?

    谢谢!

    我知道我可以在帖子中使用嵌入和存储投票数组,但我不想要,因为我的帖子可能会增长,我可能会遇到其他性能问题

2 个答案:

答案 0 :(得分:2)

正如许多人所说的那样,当模型间关系变得重要时,NoSQL并不是最好的工作;那就是说,让我们尝试列出你的两个重要业务目标:

  • 高性能查询投票计数的帖子
  • 原子或并发安全/准确性对您很重要

当然,你是在正确的轨道上缓存计数 - 但如果准确性对你很重要,那么我会做两件事:

  1. 不是直接递增或递减缓存的votes字段,而是使用适当索引的Votes集合上的查询填充
  2. 使用createdAtupdatedAt样式时间戳来确保缓存的结果仅在上一个时间之后被计算值替换:)是的,它不是原子的,但是它确实有助于确保只缓存最相关的值(以毫秒精度衡量)
  3. 现在,如果你想要全力以赴,那么适当的做法就是拥有一个与cron不同的单一服务,每隔几分钟就会针对那些具有较新updatedAt s的服务。然而,聪明地完成(2)应该使这不必要。

    希望这有帮助!

    示例

    Posts

      

    _id:“f00b4r”,数据:{...},投票:0,createdAt:“2016-05-23T07:27:33.043Z”,更新时间:“2016-05-23T07:27:33.043Z”

    现在,用户点击向下投票,所以发生了以下情况:

    Votes

      

    _id:“sn4fuu”,user_id:“j0hnd03”post_id:“f00b4r”,值:-1,createdAt:“2016-05-23T07:28:33.043Z”,更新时间:“2016-05-23T07:28 :33.043Z“

    我们的代码(比如说NodeJS)现在也会触发Votes post_id: f00b4r的所有-1(一个索引字段,速度!)的查询,它的值为Posts。< / p>

    我们以编程方式保存,以便updatedAt现在看起来像这样:

      

    _id:“f00b4r”,数据:{...},票数:-1,createdAt:“2016-05-23T07:27:33.043Z”,更新时间:“2016-05-23T07:28:33.043Z “

    请注意updatedAt从07:27到07:28已经消失了!

    我们使用Posts集合中的最新updatedAt填充Votes上的updatedAt,以确保最终缓存的投票中只有最新的更新结果 - 伯爵(是的,你认为你在这里也需要一个索引是正确的)!如果我们的代码找到比它要保存的更新的Votes,它会意识到它已经过时的信息并且不会脏写它!

    现在,如果用户将向下投票更改为向上投票

    Posts

      

    _id:“sn4fuu”,user_id:“j0hnd03”post_id:“f00b4r”,值:1,createdAt:“2016-05-23T07:28:33.043Z”,更新时间:“2016-05-23T07:29: 33.043Z“

    Votes

      

    _id:“f00b4r”,数据:{...},投票:1,createdAt:“2016-05-23T07:27:33.043Z”,更新时间:“2016-05-23T07:29:33.043Z”

    由于我们每次都在查询myView.accessibilityIdentifier = @"YOYO"; ,因此我们也可以获得复杂场景的合法值,因为我们不会直接递增或递减缓存。

答案 1 :(得分:-2)

投票:

[{ 
    _post: ObjectId(1),
    _user: [ObjectId(...),ObjectId(...),ObjectId(...),ObjectId(...)],       
}, {
    _post: ObjectId(2),
    _user: [ObjectId(...),ObjectId(...),ObjectId(...),ObjectId(...)],
}]