MongoDB如何解决16MB以上文档大小的问题?

13

我正在处理的MongoDB集合从手机中获取传感器数据,并且每2-6秒钟发送到服务器。

这些数据量很大,4-5小时后就会超过16MB的限制,看起来没有任何解决方法?

我尝试在Stack Overflow上搜索并查看了各种问题,但没有人真正分享他们的技巧。

是否有任何方式...可能是在DB端,可以像通过gridFS对大文件进行操作一样分配块?


4
具有无限增长的文档是一种反模式;您可能需要重新考虑您的数据模型,以更好地支持您的使用情况。如果您存储的是大型二进制块,则GridFS方法才是适合的;这对于您计划查询其字段的数据并不有用(除非在GridFS中仅限于关于二进制文件的元数据的查询)。对于架构建议,您需要发布一个示例文档并描述常见的更新和查询。您的MongoDB服务器版本和配置的存储引擎也将是相关的。 - Stennie
2个回答

34
为解决这个问题,您需要对数据结构进行一些小的修改。根据您所说的情况,为了超出16mb的限制,您必须将传感器数据嵌入到单个文档的数组中。
我不建议在这里使用GridFS,我认为这不是最好的解决方案,原因如下。
有一种称为分桶(bucketing)的技术,您可以使用它将传感器读数分离成单独的文档,从而解决此问题。
其工作原理如下:
假设我有一个带有特定传感器的嵌入式读数的文档,看起来像这样:
{
    _id : ObjectId("xxx"),
    sensor : "SensorName1",
    readings : [
        { date : ISODate("..."), reading : "xxx" },
        { date : ISODate("..."), reading : "xxx" },
        { date : ISODate("..."), reading : "xxx" }
    ]
}

在上述结构中,已经存在一个主要缺陷,即读取数组可能会呈指数增长,并超出16mb文档限制。

因此,我们可以稍微改变结构,使其看起来像这样,包含一个计数属性:

{
    _id : ObjectId("xxx"),
    sensor : "SensorName1",
    readings : [
        { date : ISODate("..."), reading : "xxx" },
        { date : ISODate("..."), reading : "xxx" },
        { date : ISODate("..."), reading : "xxx" }
    ],
    count : 3
}
这个想法是,当你向嵌入式数组中$push读取的时候,每次执行推送操作时,你都会增加($inc)计数变量。当你执行此更新(push)操作时,你需要在这个"count"属性上包含一个过滤器,它可能看起来像这样:

这背后的思路是,在你将阅读内容$push到嵌入式数组中时,你需要为每次执行的推送操作增加($inc)计数变量。并且在执行此更新(push)操作时,你需要在该"count"属性上包含一个过滤器,可能如下所示:

{ count : { $lt : 500} }

然后,设置您的更新选项,以便您可以将“upsert”设置为“true”:

db.sensorReadings.update(
    { name: "SensorName1", count { $lt : 500} },
    {
        //Your update. $push your reading and $inc your count
        $push: { readings: [ReadingDocumentToPush] }, 
        $inc: { count: 1 }
    },
    { upsert: true }
)

有关MongoDb Update和Upsert选项的更多信息,请参见此处:

MongoDB更新文档

当过滤条件不满足时(即当此传感器没有现有文档或计数大于或等于500时 - 因为每次推送项目时都会增加计数),将创建一个新文档,并且读数现在将嵌入到此新文档中。因此,如果正确执行此操作,则永远不会达到16mb限制。

现在,当查询特定传感器的读数时,您可能会收到该传感器的多个文档返回(而不是一个包含所有读数的文档),例如,如果您有10,000个读数,则会返回20个文档,每个文档包含500个读数。

然后,您可以使用聚合管道和$unwind来过滤读数,就像它们是自己的单独文档一样。

有关unwind的更多信息,请参见此处,它非常有用

MongoDB Unwind

希望这可以帮助您。


2
这是在这里前进的最佳方式。要了解更多关于处理此确切用例的存储桶技术,请访问此处: https://www.mongodb.com/blog/post/building-with-patterns-the-bucket-pattern - Kartavya Ramnani
1
谢谢!这实际上是我一直在寻找的一个绝妙的解决方案。 - Aleksandr Skobeltcyn
@pieperu,您能否提供一些通过聚合或其他方法提取数据的示例?聚合结果的限制是否也适用于16MB? - Aleksandr Skobeltcyn
这个策略需要在计数字段上建立索引吗?(或者是复合索引名称+计数?) - Fabricio
是的。每次读取字段时,都应该被索引覆盖。在这种情况下,它作为更新条件的一部分被读取。 - pieperu

0

你可以使用 MongoDB 中的 GridFS 来处理这种类型的情况。

GridFS 将文件分成多个部分或块1,并将每个块作为单独的文档存储,而不是将文件存储在单个文档中。默认情况下,GridFS 使用 255 kB 的块大小;也就是说,GridFS 将一个文件分成 255 kB 的块,最后一个块除外。最后一个块仅尽可能大。同样,小于块大小的文件只有最后一个块,使用尽可能少的空间加上一些附加元数据。

GridFS 的文档包含了实现 GridFS 所需的几乎所有内容。您可以按照文档进行操作。

由于您的数据是流数据,因此可以尝试以下方法...

gs.write(data, callback)

当数据是缓冲区或字符串时,回调函数会得到两个参数 - 一个错误对象(如果发生错误)和结果值,该值指示写入是否成功。只要GridStore没有关闭,每次写入都会附加到已打开的GridStore中。
您可以访问GitHub页面以获取有关流处理的相关信息。

数据每1-2秒被ping一次,因此如果我们决定缓冲它并创建一个文件,可能会干扰进程,并且应用程序到服务器的有效载荷也是一个因素。 - DeathNote
你的数据是以流的形式传输吗? - PaulShovan
是的,通过套接字。 - DeathNote

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接