【问题标题】:Performance on denormalized field vs. use of separate collection非规范化字段与使用单独集合的性能
【发布时间】:2014-11-01 05:24:31
【问题描述】:

我正在考虑跟踪每位用户的分数/分数。从我看到的例子来看,只跟踪一个字段中某些东西的总数似乎很正常。但是,我担心在作弊的情况下能够回溯或跟踪给予用户的分数/分数。以下是我的想法:

Meteor.User 集合:

Meteor.user.profile: {
    ...
    totalScore: 0
    ...
}

场景 1: 只需添加总分并跟踪每个用户:

updateScore() {
       var currentUser = Meteor.user();

       currentUser.profile.update({ _id: this._id }{$inc: { totalScore: 1} });
}

场景 1: 先将分数放入单独的 Collection 中进行记录,然后再添加到用户的总分中:

分数收集:

Scores: {
    playerId: ,
    score: ,
    ...
}


updateScore() {
       var currentUser = Meteor.user();
       Scores.insert({ playerId: this._id, score: 1, ...});
       currentUser.profile.update({ _id: this._id }{$inc: { totalScore: 1} });

       //if not the above, then thisor this

       var currentUserScore = Calculate total score count from Scores collection of current user here
       Meteor.user.profile.update({ _id: this._id }{$set: { totalScore: currentUserScore} });

}

所以我想知道的是,场景 2 与场景 1 相比是否有意义?如果场景 2 有意义,如果我通过变量 currentUserScore 计算总分,然后使用它来更新用户的 totalScore 配置文件字段(每次需要更新分数时都会运行),这是否会损害应用程序的性能?

【问题讨论】:

  • 关于问题参数的一些问题:如果玩家要作弊,他/她会怎么做?分数总是以 1 递增吗?显示分数历史是否有任何价值,或者您是否希望将其严格用于审计目的?分数增加有多常见(这像弹球还是像堆栈溢出)?
  • 嗨,大卫,回答您的问题 1. 作弊或玩弄系统 - 不知道他们将如何做,但如果他们这样做,我认为交易历史可能会有用。 2. 至于分数总是加 1 - 这只是一个例子:)。 3. 可能想要更多地利用历史记录(例如,后端或前端查看器用于任何目的) 4. 可能类似于堆栈溢出。
  • 我只是不确定我一直在阅读的这个 .aggregate() 函数是否会占用如此多的处理能力...我确实得出结论,为此类日志设置了一个单独的表有用(用于统计信息等) - 即使只是订阅日志以获取当前登录用户的数据。

标签: mongodb meteor


【解决方案1】:

根据我们的讨论,场景 2 对我来说最有意义,尤其是考虑到分数历史可能在审计总数之外具有价值。请记住,删除数据总是比创建数据更容易,因此即使历史记录没有被证明是有用的,稍后删除集合也没有什么坏处。

我会像这样实现addScore 方法:

Meteor.methods({
  addScore: function(score) {
    check(score, Number);

    Meteor.users.update(this.userId, {
      $inc: {'profile.totalScore': score}
    });

    Scores.insert({
      playerId: this.userId,
      score: score,
      createdAt: new Date()
    });
  }
});

除非您能想到一个令人信服的理由这样做,否则我怀疑每次聚合 totalScore 的数据库/计算开销是不值得的。这样做只能解决用户通过直接更新她的个人资料而作弊的情况。您可以通过添加以下内容来解决该问题:

Meteor.users.deny({
  update: function() {
    return true;
  }
});

无论您使用哪种解决方案,我都建议您添加以上内容,因为即使 insecure 已被删除,用户也可以直接更新用户配置文件。有关详细信息,请参阅文档的 this section

最后,如果您想审核每个用户的totalScore,您可以将总数汇总为nightly process 的一部分,而不是每次添加新分数时。您可以通过获取Scores 文档在服务器上执行此操作,或者使用aggregation 直接在mongodb 中执行此操作。请注意,后者需要您使用流星之外的进程(我的理解是流星的聚合包目前不起作用,但您可能想自己研究)。

【讨论】:

  • 感谢大卫的代码建议。我最终得到了或多或少相同的东西 - 呵呵,但你的建议证实了我的想法。我正在考虑只将“冗余”数据保留在最多 2 个集合中以避免混淆 - 就像上面一样 - 一个集合中的总分和另一个集合中的个人分数。是的,鉴于我们并不完全拥有丰富的资源,这些天似乎预先计算事情是需要认真考虑的事情。
  • 哦,关于上述拒绝规则的一个问题。由于我确实想让用户访问他们的“个人资料页面”,我仍然可以拒绝对特定字段的更新吗?
  • 拒绝更新仍将发布数据。您可以坚持上述方法并使用修改用户配置文件的方法,也可以检查fieldNames 参数以允许/拒绝并允许客户端修改。
猜你喜欢
  • 1970-01-01
  • 2016-05-13
  • 2018-06-06
  • 2013-03-29
  • 1970-01-01
  • 1970-01-01
  • 2012-10-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多