【问题标题】:How to save an JSON file using GridFs如何使用 GridFs 保存 JSON 文件
【发布时间】:2020-07-13 01:16:48
【问题描述】:

我有一个庞大的数据集,我使用的是猫鼬模式,每个数据元素看起来像这样:

    {
      field1: “>HWI-ST700660_96:2:1101:1455:2154#5@0/1”: 
      field2: “GAA…..GAATG”

    }

来源:Reading an FASTA file

如您所见,单个元素简单而小,但数量巨大!它们加起来将超过 200MB。

问题是:我无法将它保存到 mongo,因为它太大(> 200MB)

不过,我找到了 GridF,

  • 到目前为止,我找到的所有材料都是关于图像和视频上传的;

  • 他们没有说我怎么还能使用猫鼬模式功能;

  • 到目前为止,我看到的示例并没有将数据保存到用户定义的路径中,就像我们使用猫鼬所做的那样。

在最简单的场景中:如何使用 GridFS 或任何类似的解决方案来保存 JSON 文件,就像使用小型 JSON 文件一样。与其他方法相比,这种方法有哪些优点和缺点(如果有的话)?你认为我的方法有效吗?我的意思是,我在这里提到的那个,使用 JSON 文件树和稍后的populate,它可以工作!

作为使用 mongoose 保存 JSON 文件的示例:

Model.create([        
          {
          field1: “>HWI-ST700660_96:2:1101:1455:2154#5@0/1”: 
          field2: “GAA…..GAATG”

        }, 
        {
          field1: “>HWI-ST700660_96:2:1101:1455:2154#5@0/1”: 
          field2: “GAA…..GAATG”

        }]);

这里我刚刚保存了一个包含两个元素的 JSON 文件,我不能用一个很大的文件来做到这一点,我需要分成更小的部分(比如 1% 的块),然后创建刚才提到的树,至少那是我的解决方案。

恐怕我正在重新发明轮子。我可以独立保存这些文件,它可以工作,但我需要保持它们的相关性,因为它们属于同一个文件,就像图像的较小块属于同一个图像一样。

恐怕我要重新发明轮子了。

当前解决方案

这是我目前的解决方案,使用我自己的见解! 看到我在这里提到只是出于好奇,它不使用 GridFS,因此,我仍然愿意接受使用 GridFS 的建议。它只使用 JSON 文件,并将文档分成更小的文件,就像层次结构一样。它是一棵树,我只想要溶液中的叶子。

我已经用这张图解决了这个问题,不过,出于学习目的,我想看看是否可以使用 GridFS 来做同样的事情

讨论

我的第一个方法是将它们保留为 subdoc:它失败了!然后我试图只保留他们的 id,他们的 id 对应于整个块的 35%,并且大于 16MB:失败!然后我决定创建一个虚拟文档,仅保留 id,并仅存储虚拟文档的 id:成功!

【问题讨论】:

  • 如果我的回答对你有帮助,请告诉我
  • 是的,我几乎对它已经 5 岁的事实发表了评论。尽管如此,我认为 GridFS 在这方面没有改变。检索/存储时间与文档大小成正比这一事实与 GridFS 的设计有关,即它将数据拆分为块的方式。所以 5 年后,据我所知,这方面没有任何改变。
  • 希望你能得到其他更有见地的答案!
  • 您可以使用 GridFS 在 Mongo 中存储文件。如果使用 GridFS,无论文件中的数据是什么类型,都无法查询。您不能使用 find()save() 或任何其他 Collection 方法访问使用 GridFS 保存的文件中的数据。您可以仍然使用find()和其他Collection数据来查询/访问GridFS存储文件的元数据,其中包含文件大小、文件名、块数,以及您希望与文件一起保存的任何其他数据。您仍然可以在任何常规文档(不是 GridFS 文件)上使用 Collection 方法
  • 谢谢,现在事情似乎更清楚了。我会尽快测试您的见解,上次我尝试过,我没有成功。最好的学习方式是编码!谢谢。

标签: javascript json mongodb mongoose


【解决方案1】:

很可能不值得使用 GridFS 将数据存储在 Mongo 中。

二进制数据从不真正属于数据库,但如果数据很小,将其放入数据库(查询能力)的好处大于缺点(服务器负载、速度慢)。

在这种情况下,您似乎希望将文档数据 (JSON) 存储在 GridFS 中。您可以这样做,并以存储任何其他二进制数据的方式存储它。然而,数据将是不透明的。您不能查询存储在 GridFS 文档中的 JSON 数据,只能查询文件元数据。

查询大数据

正如您提到要查询数据,您应该检查数据的格式。如果您的数据采用示例中列出的格式,那么似乎不需要复杂的查询,只需要字符串匹配。所以有几种选择。

案例一:大数据,点少

如果您的数据集不多(field1field2 对),但每个数据集都很大(field2 包含很多字节),请将它们存储在其他位置并仅存储对其的引用。一个简单的解决方案是将数据(以前的 field2)存储在 Amazon S3 上的文本文件中,然后存储然后存储链接。例如

{
  field1: “>HWI-ST700660_96:2:1101:1455:2154#5@0/1”,
  field2link: "https://my-bucket.s3.us-west-2.amazonaws.com/puppy.png"
}

案例2:小数据,多点

如果您的每个数据集都很小(小于 16 MB)但有很多数据集,请将您的数据存储在 MongoDB(不带 GridFS)中。

细节

在您的情况下,数据非常大,不建议使用 GridFS 存储它。

This answer 提供了一个向下的基准。基准似乎表明检索时间或多或少与文件大小成正比。使用相同的设置,从数据库中检索文档需要 80 秒。

可能的优化

GridFS 中的默认块大小为 255 KiB。您可以通过将块大小增加到最大值 (16 MB) 来减少大文件访问时间。如果块大小是唯一的瓶颈,那么使用 16 MB 块大小会将检索时间从 80 秒减少到 1.3 秒 (80 / (16MB/255KiB) = 1.3)。您可以在初始化 GridFS 存储桶时执行此操作。

new GridFSBucket(db, {chunkSizeBytes: 16000000})

更好的策略是将唯一的文件名存储在 Mongo 中,然后从文件系统中检索文件。

其他缺点

在 Mongo 中存储二进制数据的另一个可能的缺点来自this site: “如果二进制数据很大,那么将二进制数据加载到内存中可能会导致频繁访问的文本(结构化数据)文档被挤出内存,或者更一般地说,工作集可能无法放入 RAM。这会对数据库的性能。” [1]

示例

在GridFS中保存文件,改编自Mongo GridFS tutorial

const uri = 'mongodb://localhost:27017/test';

mongodb.MongoClient.connect(uri, (error, db) => {
  const bucket = new mongodb.GridFSBucket(db);

  fs.createReadStream('./fasta-data.json')
    .pipe(bucket.openUploadStream('fasta-data.json'))
    .on('finish', () => console.log('done!'))
  ;
});

【讨论】:

  • “更好的策略是将唯一的文件名存储在 Mongo 中,然后从文件系统中检索文件。”您的意思是像我们每天保存的任何文件一样正常保存文件,然后使用链接检索它?
  • 将其保存在提供冗余且可从服务器所在位置访问的位置上。 Amazon S3 是一个不错的选择。我已经更新了答案
  • 你好,我看到你已经更新了答案。我的情况似乎与 case 2 很接近;文件本身很小,只有 2-4 个字段;我可以独立保存它们,没问题,大约需要 9 分钟。但是……我需要以某种方式连接它们。我的第一个解决方案是保留他们的个人 ID,但也太大了!然后我决定像一棵树一样做:保留一个虚拟文档的 id 来保留他们的 id。 “如果您的每个数据集都很小(小于 16 MB)但有很多数据集,请将您的数据存储在 MongoDB(没有 GridFS)中。”你能更好地解释一下吗?
  • @JorgePires small 表示 字节,而不是字段数。您的示例数据只有 2 个字段,并没有指示每个字段的大小(以字节为单位)。请说明您有多少数据点/集(数组大小,如问题中发布的示例)以及每个数据点/集的总数据字节数的最小和最大大小
  • "如果您的每个数据集都很小(小于 16 MB)但有很多数据集,请将您的数据存储在 MongoDB(没有 GridFS)中。我相信这就是我所做的!
【解决方案2】:

我找到了一种比我已经实施的方法(问题描述中的方法)更好的方法来解决这个问题。我只需要使用虚拟机!

首先我认为使用ForEach 向 Fasta 文件添加额外元素会很慢,不是,它非常快!

我可以为每个 Fasta 文件做这样的事情:

{
  Parentid: { type: mongoose.Schema.Types.ObjectId, ref: "Fasta" }//add this new line with its parent id
  field1: “>HWI-ST700660_96:2:1101:1455:2154#5@0/1”: 
  field2: “GAA…..GAATG”

}

然后是这样的:

FastaSchema.virtual("healthy", {
  ref: "FastaElement",
  localField: "_id",
  foreignField: "parent",
  justOne: false,
});

最终填充:

  Fasta.find({ _id: ObjectId("5e93b9b504e75e5310a43f46") })
    .populate("healthy")
    .exec(function (error, result) {          
      res.json(result);
    });

魔术就完成了,子文档超载没问题!应用于 Virtual 的填充速度非常快,并且不会导致过载!我没有这样做,但是与传统的填充比较会很有趣;但是,这种方法的优点是不需要创建隐藏的文档来存储 id。

我对这个简单的解决方案感到无语,当我在这里回答另一个问题时出现,它就出现了!

感谢猫鼬!

【讨论】:

  • 感谢您的见解!我从你身上学到了很多!我很确定您的见解会派上用场!
猜你喜欢
  • 1970-01-01
  • 2016-01-07
  • 2011-10-19
  • 2012-04-29
  • 2012-11-17
  • 2012-06-27
  • 2014-03-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多