MongoDB:如果使用$addToSet或$push,是否应该预分配文档?
我一直在研究MongoDB,我知道强烈建议在插入时完全构建(预先分配)文档结构,这样以后对该文档的更改就不需要在磁盘上移动文档。使用$addToSet或$push时是否适用 例如,假设我有以下文档:MongoDB:如果使用$addToSet或$push,是否应该预分配文档?,mongodb,meteor,Mongodb,Meteor,我一直在研究MongoDB,我知道强烈建议在插入时完全构建(预先分配)文档结构,这样以后对该文档的更改就不需要在磁盘上移动文档。使用$addToSet或$push时是否适用 例如,假设我有以下文档: "_id" : "rsMH4GxtduZZfxQrC", "createdAt" : ISODate("2015-03-01T12:08:23.007Z"), "market" : "LTC_CNY", "type" : "recentTrades", "data" : [ {
"_id" : "rsMH4GxtduZZfxQrC",
"createdAt" : ISODate("2015-03-01T12:08:23.007Z"),
"market" : "LTC_CNY",
"type" : "recentTrades",
"data" : [
{
"date" : "1422168530",
"price" : 13.8,
"amount" : 0.203,
"tid" : "2435402",
"type" : "buy"
},
{
"date" : "1422168529",
"price" : 13.8,
"amount" : 0.594,
"tid" : "2435401",
"type" : "buy"
},
{
"date" : "1422168529",
"price" : 13.79,
"amount" : 0.594,
"tid" : "2435400",
"type" : "buy"
}
]
我正在使用以下命令之一将新的对象数组(newData
)添加到data
字段:
$addToSet要添加到数组末尾:
Collection.update(
{ _id: 'rsMH4GxtduZZfxQrC' },
{
$addToSet: {
data: {
$each: newData
}
}
}
);
Collection.update(
{ _id: 'rsMH4GxtduZZfxQrC' },
{
$push: {
data: {
$each: newData,
$position: 0
}
}
}
);
要添加到阵列前端的$push(带$position):
Collection.update(
{ _id: 'rsMH4GxtduZZfxQrC' },
{
$addToSet: {
data: {
$each: newData
}
}
}
);
Collection.update(
{ _id: 'rsMH4GxtduZZfxQrC' },
{
$push: {
data: {
$each: newData,
$position: 0
}
}
}
);
由于从newData
添加了新对象,文档中的data
数组将增长。那么,这种类型的文档更新会导致文档在磁盘上移动吗
对于这个特定的系统,这些文档中的
数据
数组在中可以增长到75k个以上的对象,因此,如果这些文档在每次$addToSet或$push更新后确实在磁盘上移动,那么文档在插入时是否应该定义75k个null(数据:[null,null…null]
),然后可能会使用$set来替换随时间变化的值?谢谢 MongoDB使用两种分配策略的威力来存储文档,这意味着它将分配文档^2的大小以进行存储。因此,如果嵌套数组的总增长量不超过原始大小的2倍,mongo就不必重新分配文档
请参阅:这里的底线是,任何“文档增长”几乎总是会导致存储分配的“物理移动”,除非您以某种方式对原始文档提交进行了“预分配”。是的,有“二次幂”分配,但这并不总是意味着对您的存储箱有效 这里的附加“捕获”是打开的,其中“隐藏捕获”实际上是这样的“预分配”方法可能不会“复制”到副本集中的其他成员,如果这些指令不在应用副本集项的“oplog”期间 如果任何结构的增长超出了“初始分配”或可应用的一般技巧所分配的内容,将导致该文档在超出最初提供的空间时在存储空间中“移动” 为了确保不会发生这种情况,您总是“预分配”原始创建数据的预期规定。并对已经描述的情况提出了明显的警告 我知道强烈建议在插入时完全构建(预分配)文档结构,这样以后对该文档的更改就不需要在磁盘上移动文档。使用$addToSet或$push时是否适用 如果它对用例是可行的,则建议使用它,而通常情况下不可行。时间序列数据是一个显著的例外。它实际上不适用于
$addToSet
和$push
,因为它们倾向于通过增加数组来增加文档的大小
这些文档中的数据数组可以在
停下来。是否确实要不断增加包含成千上万个条目的数组?您是否要查询想要返回的特定条目?是否要为数组项中的任何字段编制索引?您可能需要重新考虑文档结构。也许您希望每个数据
条目都是一个单独的文档,每个条目中都复制有市场
、类型
、创建数据
等字段?您不必担心文档移动
为什么阵列将增长到75K个条目?你能减少每个文档的输入吗?是这个吗?能够使用mmap存储引擎预先分配文档并进行就地更新是很好的,但这并不是每个用例都可行的,MongoDB也不需要这样才能很好地执行
文档是否应该在insert时定义75k个null(数据:[null,null…null]),然后可能使用$set随时间替换这些值
不,这不是很有帮助。文档大小将根据数组中null值的BSON大小来计算,因此当您用另一种类型替换null
时,该大小将增加,并且您仍将得到文档重写。您需要使用所有字段都设置为其类型的默认值的对象预先分配数组,例如
{
"date" : ISODate("1970-01-01T00:00:00Z") // use a date type instead of a string date
"price" : 0,
"amount" : 0,
"tid" : "000000", // assuming 7 character code - strings icky for default preallocation
"type" : "none" // assuming it's "buy" or "sell", want a default as long as longest real values
}
您是否建议在插入时使用75k null(
数据:[null,null…null]
)定义文档,然后可能使用$set随时间使用实际对象替换null值?而不是使用$addToSet随时间动态增长文档?这能解决问题吗?谢谢@JonCursi当然,当您提交具有相同“设置”结果的多个值时,这些值将被服务器本身作为$addToSet
的过程“取消”,因为它们是相同的。但不建议这样做,您可能应该在提交之前在客户机代码中对其进行分类。2分配的威力并不一定能消除阵列增长超过这些长度的问题。最好的情况是评估您的使用模式,并根据需要根据实际的存储需求进行设计/预分配。感谢您的回复,这非常有帮助!是的,这是时间序列数据。我根据新加入的对象(大约1个对象/秒)生成了几个课程解决方案,这些文档就是客户端订阅中使用的文档。但我正试图找出存储原始对象的最佳方式,我只想保留这些对象,以备将来需要它们作为参考,即由于系统故障等原因需要重新生成课程分辨率。存储hundr的最佳方式是什么