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



我有一个应用程序,它监听websocket,并存储用户名/userID(用户名为1-20字节,userID为17字节)。这没什么大不了的,因为这只是一份文件。然而,他们参与的每一轮,它都会推送轮ID(24字节)和一个"分数"十进制值(例如:1190.001523999999)。

问题是,有多少轮是没有限制的,我负担不起每月为蒙古支付这么多钱。处理这些数据的最佳方式是什么?

我的想法:-如果有一种方法可以替换mongodb中的_id:字段,我会将其替换为17字节长的userID。但不确定我是否能做到。

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

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

  • 完全删除Round ID(或将_ID字段替换为roundId)(不起作用,因为每个文档中有多个roundID)

  • 将十进制值四舍五入到两位。

  • 30天后删除回合ID

tl;dr

  • 需要高效地存储数据<500 mb在mongo实验室

  • 文档由用户名(1-20个字符)、用户Id(17个字符)和圆形(对象数组)=[{圆形Id(24个字符),分数(1190.001523999999)}]组成。

提前感谢!

编辑:

文档架构:

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

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

一个更好的(阅读更具伸缩性和效率的方法)是使用文档引用。

首先,你当然需要你的球员数据。这里有一个例子:

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

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

接下来,每轮的结果:

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

这里有几点需要注意。首先:不要使用字符串来记录值。甚至等级也应该存储为相应的数值。否则你就无法得到平均值和相似值。我稍后会展示更多。我们在这里为_id使用了一个复合字段,这是完全有效的。此外,它将为我们提供一个免费索引,优化一些最有可能的查询,如"玩家X在Y轮中得分如何?">

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

或者"Y轮的结果在哪里?">

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

或者"玩家X在所有回合中的得分是多少?">

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可以更有效地进行计算,因为数据在处理之前不必发送到你的应用程序。

接下来,针对数据过期。我们有一个类型为ISODate的createdAt字段。现在,我们让MongoDB通过创建TTL索引来处理其余部分

db.results.ensureIndex(
{ "createdAt":1 },
{ expireAfterSeconds: 60*60*24*30}
)

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

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

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

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

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

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

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

round[{score:String}]

最新更新