MongoDB:如果使用$ addToSet或$ push,您是否应该预先分配文档?

时间:2015-03-02 11:36:40

标签: mongodb meteor

我一直在研究MongoDB,我理解强烈建议文档结构在插入时完全构建(预先分配),这样未来对该文档的更改不需要文档在磁盘上移动。这在使用$ addToSet或$ push?

时是否适用

例如,假设我有以下文件:

"_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
      }
    }
  }
);

$ push(带$ position)添加到数组的前面:

Collection.update(
  { _id: 'rsMH4GxtduZZfxQrC' },
  {
    $push: {
      data: {
        $each: newData,
        $position: 0
      }
    }
  }
);

由于从data添加的新对象,文档中的newData数组将会增长。那么这种类型的文档更新会导致文档在磁盘上移动吗?

对于这个特定的系统,这些文档中的data数组可以增长到75k以上的对象,所以如果这些文档确实在每次$ addToSet或$ push更新后在磁盘上移动,那么文档是否应该在插入时使用75k空值(data: [null,null...null])定义,然后可能使用$ set来替换值随时间变化?谢谢!

3 个答案:

答案 0 :(得分:2)

  

据我所知,强烈建议在插入时完全构建(预先分配)文档结构,这样未来对该文档的更改不需要在磁盘上移动文档。这在使用$ addToSet或$ push?

时是否适用

如果它对用例通用是可行的,通常情况并非如此。时间序列数据是一个值得注意的例外。它并不适用于$addToSet$push,因为它们会通过增加数组来增加文档的大小。

  

这些文档中的数据数组可以增长到75k以上的对象

停止。你确定你想要不断增长的阵列有成千上万的条目吗?您是否要查询想要的特定条目?你要索引数组条目中的任何字段吗?您可能想重新考虑您的文档结构。也许您希望每个data条目都是一个单独的文档,其中每个字段都会复制markettypecreatedAt等字段?你不会担心文件的移动。

为什么数组会增长到75K条目?你能为每份文件减少参赛作品吗?这是time series data吗?能够预先分配文档并使用mmap存储引擎进行就地更新真是太棒了,但是对于每个用例都不可行,并且MongoDB不能很好地执行

  

应该在插入时使用75k nulls(data:[null,null ... null])定义文档,然后使用$ set来替换值随着时间的推移?

不,这不是真的有用。文档大小将根据数组中空值的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
}

答案 1 :(得分:1)

MongoDB使用两种分配策略来存储您的文档,这意味着它将分配文档^ 2的大小进行存储。因此,如果您的嵌套数组不会导致总增长大于原始大小到2的幂,则mongo将不必重新分配文档。

请参阅:http://docs.mongodb.org/manual/core/storage/

答案 2 :(得分:1)

这里的底线是,任何“文档增长”几乎总是会导致存储分配的“物理移动”,除非您通过某种方式在原始文档提交上“预先分配”。是的,有“两个权力”的分配,但这并不总是意味着对你的存储案件有效。

这里的额外“捕获”在"capped collections"上,其中“隐藏的捕获”确实是这样的“预分配”方法可能不会“复制”到副本集中的其他成员,如果这些指令不属于应用副本集条目的“oplog”期间。

增加任何超出“初始分配”分配的结构或可以应用的一般技巧导致该文档在存储空间超出空间时被“移动”最初是随附的。

为了确保不会发生这种情况,您始终会“预分配”到原始创建的数据的预期条款。并且已经描述了这种情况的明显警告。