增长文档时 MongoDB 中的碎片

问题描述 投票:0回答:3

看起来带有评论的博客是用于描述使用 MongoDB 时不同建模策略的标准示例。

我的问题与模型有关,其中评论被建模为单个博客文章文档的子集合(即一个文档存储与单个博客文章相关的所有内容)。

在多个同时写入的情况下,如果使用 upsert 和目标更新修饰符(如推送),似乎可以避免覆盖以前的更新。这意味着,为添加的每个评论保存文档不会覆盖以前添加的评论。 然而,碎片化在这里如何发挥作用呢?假设随着时间的推移添加多个注释会导致内存碎片和可能较慢的查询是否现实? 是否有通过子集合生成文档的任何指南?

我也知道每个文档 16MB 的限制,但对我来说这似乎是一个理论上的限制,因为 16MB 将是一个巨大的文本量。 如果出现碎片,下次 mongo 实例重新启动时,文档是否会被压缩并将数据库读回内存?

我知道您期望与数据交互的方式是如何对数据进行建模的最佳指导原则(需要没有博客文章父级的评论等)。然而,我有兴趣了解高度非规范化的单文档方法的潜在问题。我在给定的博客文章示例中描述的问题是否现实?

mongodb data-modeling denormalization nosql
3个回答
4
投票

在回答你的问题之前,我尝试大概解释一下MongoDB的存储机制。

  • 对于某个数据库test,可以看到一些类似
    test.0, test.1, ...
    的文件,所以DATABASE = [FILE, ...]
  • 文件 = [范围,...]
  • 范围 = [记录,...]
  • 记录=标题+文档+填充
  • 标头 = 大小 + 偏移量 + 上一个记录指针 + 下一个记录指针 + 标志 + ...

此链接供您参考

现在我尝试尽可能回答您的一些问题。

  1. 碎片化是如何发挥作用的?
    当当前记录不足以存储更新的文档时,就会发生这种情况,然后产生一个迁移,其行为是将更新的文档存储到新的足够的空间中,并删除原始记录。删除的记录变成了一个片段。

  2. 会导致内存碎片和查询速度变慢吗?
    就会出现内存碎片。但它不会导致查询速度变慢,除非最终没有足够的内存来分配。

但是,如果新的文档可以容纳已删除的记录,则可以重新使用该记录。下面是一个简单的确凿证明。
(注意填写的offset

> db.a.insert([{_id:1},{_id:2},{_id:3}]);
BulkWriteResult({
        "writeErrors" : [ ],
        "writeConcernErrors" : [ ],
        "nInserted" : 3,
        "nUpserted" : 0,
        "nMatched" : 0,
        "nModified" : 0,
        "nRemoved" : 0,
        "upserted" : [ ]
})
> db.a.find()
{ "_id" : 1 }
{ "_id" : 2 }
{ "_id" : 3 }
> db.a.find().showDiskLoc()
{ "_id" : 1, "$diskLoc" : { "file" : 0, "offset" : 106672 } }
{ "_id" : 2, "$diskLoc" : { "file" : 0, "offset" : 106736 } }   // the following operation will delete this document
{ "_id" : 3, "$diskLoc" : { "file" : 0, "offset" : 106800 } }
> db.a.update({_id:2},{$set:{arr:[1,2,3]}});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.a.find().showDiskLoc()
{ "_id" : 1, "$diskLoc" : { "file" : 0, "offset" : 106672 } }
{ "_id" : 3, "$diskLoc" : { "file" : 0, "offset" : 106800 } }
{ "_id" : 2, "arr" : [ 1, 2, 3 ], "$diskLoc" : { "file" : 0, "offset" : 106864 } }  // migration happened
> db.a.insert({_id:4});
WriteResult({ "nInserted" : 1 })
> db.a.find().showDiskLoc()
{ "_id" : 1, "$diskLoc" : { "file" : 0, "offset" : 106672 } }
{ "_id" : 3, "$diskLoc" : { "file" : 0, "offset" : 106800 } }
{ "_id" : 2, "arr" : [ 1, 2, 3 ], "$diskLoc" : { "file" : 0, "offset" : 106864 } }
{ "_id" : 4, "$diskLoc" : { "file" : 0, "offset" : 106736 } }   // this space was taken up by {_id:2}, reused now.
>

1
投票

此外,您应该阅读Asya Kamsky的这篇文章。它可以帮助您做出决定。 http://askasya.com/post/largeembeddedarrays

最明显的问题是最终你会达到 16MB 文件限制,但这根本不是您应该担心的 关于。不断增长的文档会导致越来越高 每次必须在磁盘上重新定位时都会花费成本,即使您采取 减轻碎片影响的步骤,您的写入将 整体上不必要地长,影响你的整体性能 整个应用程序。


0
投票

用于碎片检查

dinesh> db.stats()

{

db:'迪内什',

集合:长('0'),

观看次数:长('0'),

对象:长('0'),

avgObjSize:0,

数据大小:0,

存储大小:0,

索引:Long('0'),

索引大小:0,

总大小:0,

scaleFactor:长('1'),

fsUsedSize:0,

fsTotalSize:0,

好的:1 }

© www.soinside.com 2019 - 2024. All rights reserved.