在MongoLab和一般情况下有效地存储数据

时间:2015-04-13 21:23:19

标签: node.js mongodb heroku mlab database

我有一个应用程序,它监听websocket并存储用户名/用户ID(用户名是1-20个字节,用户ID是17个字节)。这不是什么大问题,因为它只有一个文档。但是,在他们参与的每一轮中,它都会推送圆形ID(24个字节)和一个“得分”十进制值(例如:1190.0015239999999)。

问题是,对于有多少回合没有限制,而且我无法承担每月为蒙戈拉支付这么多钱的费用。处理这些数据的最佳方法是什么?

我的想法: - 如果有办法替换mongodb中的_id:字段,我将用17字节长的userID替换它。不知道我是否可以这样做。

  • 使用时间戳存储用户数据,并删除分数值小于200的OLD数据。

  • 切断超过10个字符的用户名。

  • 完全删除Round ID(或用roundId替换_id字段)。(由于每个文档中有多个roundID,因此无效)

  • 将小数值四舍五入到两个位置。

  • 30天后删除回合ID

tl; dr

  • 需要有效地存储数据< mongo lab 500 mb

  • 文件包括用户名(1-20个字符),用户ID(17个字符),圆形(对象数组)= [{round Id(24个字符),得分(1190.0015239999999)}]。

    < / LI>

提前致谢!

修改

文档架构:

userID: {type: String},
userName: {type: String},
rounds: [{roundID: String, score: String}]

2 个答案:

答案 0 :(得分:1)

建模1:n作为嵌入式文档的关系不是最好的,除非极少数情况。这是因为在撰写本文时,BSON文档的大小限制为16MB。

更好(阅读更具可扩展性和效率的方法)是使用document references

首先,您需要您的播放器数据。这是一个例子:

{
  _id: "SomeUserId",
  name: "SomeName"
}

不需要额外的userId字段,因为每个文档都需要具有唯一值的_id字段。与流行的看法相反,这个字段值不一定是ObjectId。因此,如果我没有弄错的话,我们已经将玩家数据所需的大小减少了1/3。

接下来,每轮结果:

{
  _id: {
    round: "SomeString",
    player: "SomeUserId"
  },
  score: 5,
  createdAt: ISODate("2015-04-13T01:03:04.0002Z")
}

这里要注意一些事情。首要的是: NOT 使用字符串来记录值。甚至等级也应该存储为相应的数值。否则你无法获得平均值等。我稍后会再说明一点。我们在这里使用_id的复合字段,这是完全有效的。此外,它将为我们提供一个免费索引,优化一些最有可能的查询,例如“玩家X如何在Y轮中得分?”

db.results.find({"_id.player":"X","_id.round":"Y"})

或“Y轮的结果在哪里?”

db.results.find({"_id.round":"Y"})

或“我们在各轮比赛中得分是什么?”

db.results.find({"_id.player":"X"})

但是,使用字符串来保存分数,即使是一些漂亮的统计数据也变得相当便宜,例如“Y轮的平均得分是多少?”

db.results.aggregate(
  { $match: { "_id.round":"Y" } },
  { $group: { "round":"$_id.round", "averageScore": {$avg:"$score"} }
)

或“各轮次中每位球员的平均得分是多少?”

db.results.aggregate(
  { $group: { "player: "$_id.player", "averageAll": {$avg:"$score"} }
)

虽然您可以在应用程序中执行这些计算,但MongoDB可以更有效地执行这些计算,因为在处理数据之前不必将数据发送到您的应用程序。

接下来,对于数据过期。我们有一个createdAt字段,类型为ISODate。现在,我们让MongoDB通过创建TTL index

来处理剩下的事情
db.results.ensureIndex(
  { "createdAt":1 },
  { expireAfterSeconds: 60*60*24*30}
)

总而言之,这应该是存储和过期数据的最有效方式,同时提高可扩展性。

答案 1 :(得分:0)

因此,目前您在数组中为每条记录存储三个数据点。

_id:false将阻止mongoose自动为文档创建id。如果您不需要roundID,那么您可以使用以下仅在数组中存储一个数据点的内容:

round[{_id:false, score:String}]

否则如果roundID实际上有意义,请使用以下内容在数组中存储两个数据点:

round[{_id:false, roundID: string, score:String}]

最后,如果您只需要一个ID用于参考,请使用以下内容,它将在数组中存储两个数据点 - 随机ID和分数:

round[{score:String}]