在增长文档时MongoDB中的碎片

时间:2014-09-25 01:33:47

标签: mongodb data-modeling denormalization nosql

似乎是一个带有注释的博客是用于描述使用MongoDB时不同建模策略的标准示例。

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

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

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

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

2 个答案:

答案 0 :(得分:1)

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

  • 对于某个数据库测试,您可以看到一些文件,例如test.0, test.1, ...,因此 DATABASE = [文件,...]
  • FILE = [EXTENT,...]
  • EXTENT = [记录,...]
  • RECORD = HEADER + DOCUMENT + PADDING
  • HEADER = SIZE + OFFSET + PREV_RECORD_POINTER + NEXT_RECORD_POINTER + FLAG + ...

This link for your reference

现在我尽可能地回答你的一些问题。

  1. 碎片是如何形成的? 当当前记录不足以存储更新的文档,然后产生具有将更新的文档存储到新的足够空间并删除原始记录的行为的迁移时,会发生这种情况。删除的记录结果是一个片段。

  2. 是否会导致内存碎片和查询速度变慢? 将发生碎片化的内存。但它不会导致查询速度变慢,除非最终没有足够的内存来分配。

  3. 但是,如果新的文档可以适用,则可以重复使用已删除的记录。以下是一个简单的实证 (注意提交的偏移

    > 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 :(得分:1)

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

  

最明显的问题是最终你会达到16MB   文件限制,但这根本不是你应该关心的   关于。不断增长的文件将越来越高   每次必须重新定位在磁盘上,即使你采取的成本   你的写作会减少碎片影响的步骤   整体上不必要地长,影响你的整体表现   整个申请。