2015-03-02 66 views
1

我一直在研究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 
     } 
    } 
    } 
); 

$推(以$位置),以添加到数组的前:

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

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

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

回答

2

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

如果用例通常不可行,建议使用它。时间序列数据是一个明显的例外。它并不适用于$addToSet$push,因为它们倾向于通过增加数组来增加文档的大小。

在这些文件中的数据阵列可内

停止向上75K对象增长到。你确定你想要不断增加拥有数万条目的阵列吗?你打算查询想要返回特定条目吗?你打算索引数组条目中的任何字段吗?您可能想重新考虑您的文档结构。也许你想让每个data条目成为一个单独的文件,其中包含market,type,createdAt等字段。你不会担心文件移动。

为什么数组会增长到75K条目?你能否为每个文件做更少的条目?这是time series data?能够预先分配文档并使用mmap存储引擎进行就地更新是非常好的,但对于每个用例都不可行,并且MongoDB的性能不是很好。

应该用75k个nulls(数据:[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 
} 
+0

谢谢你的回复,这非常有帮助!是的,这是时间序列数据。我基于进入的新对象(大约1个对象/秒)生成若干课程解决方案,这些文档是客户订阅中使用的那些文档。但我试图找出存储原始对象的最佳方式,我几乎只想保留在未来的需要,即需要重新生成课程决议,因为系统故障等。存储客户端不需要的数十万个这些大小的原始对象的最佳方式是什么? – 2015-03-02 21:03:59

+0

目前,我有上述原始文章中描述的文档结构。有一个'data'字段,它是一个长度增长的数组。一旦该数组增长到75k大小的对象,我插入一个具有相同结构的新文档,并开始在那里添加拓扑。所以我累积了一堆长度为75k个对象的文档。选择75k是因为由于这个特定的对象大小,其中的75k等于〜7.5MB的文档大小,我不想接近16MB的硬限制以避免控制台警告。也许你也可以让我知道这种方法是否正确? – 2015-03-02 21:08:47

1

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

参见:http://docs.mongodb.org/manual/core/storage/

+1

2分配的功率并不一定会消除数组增长超过这些长度的问题。最好的情况是根据需要根据实际的存储需求评估您的使用模式和设计/预分配。 – 2015-03-02 11:50:59

1
这里

底线是,任何“文件增长”是几乎总是会导致存储分配的“物理移动”,除非你有“预分配”通过一些手段原始文件提交。是的,有两个“权力”分配,但这并不总是意味着任何有效的存储情况。

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

增加超出从“初始分配”分配的结构或可应用的一般技巧导致该文档在存储空间中“移动”超出其最初提供的空间。

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

+0

您是否建议在插入时用75k nulls('data:[null,null ... null]')定义文档,然后可能使用$ set用实际对象随时间替换空值?而不是使用$ addToSet随着时间的推移动态增长文档?这能解决问题吗?谢谢! – 2015-03-02 11:50:25

+0

@JonCursi当然,当你使用相同的“set”结果提交多个值时,它们将被服务器本身“废弃”,因为它们是相同的,因为它们是'$ addToSet'的过程。这不是建议,你可能应该在提交之前在客户端代码中进行分类。 – 2015-03-02 11:53:58

相关问题