【问题标题】:MongoDB query executes in 1ms on mongo-shell but takes 400ms and more on NodeJSMongoDB 查询在 mongo-shell 上执行时间为 1 毫秒,但在 NodeJS 上需要 400 毫秒甚至更多
【发布时间】:2015-06-20 06:05:15
【问题描述】:

我有一个大型 MongoDB 集合,包含超过 2GB 的原始数据,我使用一个非常简单的查询通过其 Id 从集合中获取特定文档。文档大小目前从 10KB 到 4MB 不等,Id 字段定义为索引。

这是我正在使用的查询(使用 mongojs 模块):

db.collection('categories').find({ id: category_id },
    function(err, docs) {
        callback(err, docs.length ? docs[0] : false);
    }).limit(1);

当我使用 MongoDB shell 或 Robomongo 等 GUI 执行此查询时,无论其物理大小如何,获取文档大约需要 1 毫秒,但是当我在 NodeJS 上执行完全相同的查询时,响应时间范围从 2 毫秒到2s 或更多,具体取决于数据量。我只测量接收响应所需的时间,即使在 NodeJS 等待超过 500 毫秒的情况下,MongoDB 分析器 (.explain()) 也显示执行查询只需要一毫秒。

现在,我可能做错了什么,但我不知道是什么。我对 NodeJS 比较陌生,但我过去曾使用过 MongoDB 和 PHP,而且我从未遇到过这样的性能问题,所以我倾向于认为我可能以某种方式滥用了 NodeJS。

我还尝试在 WebStorm 上使用 SpyJS 进行分析,我看到有很多 bson.deserialize 调用很快就会汇总成一个大堆栈,但我无法进一步调查,因为此时 SpyJS 总是崩溃。可能相关,但我仍然不知道如何处理它。

请告知,任何线索将不胜感激。

编辑: 这是db.categories.getIndexes()的结果:

[
    {
        "v" : 1,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "my_db.categories"
    },
    {
        "v" : 1,
        "key" : {
            "id" : 1
        },
        "name" : "id_1",
        "ns" : "my_db.categories"
    }
]

我也尝试过使用findOne,但没有任何区别:

db.collection('categories').findOne({ id: category_id },
    function(err, doc) {
        callback(err, doc || false);
    });

【问题讨论】:

  • id 字段上创建的任何索引?
  • 只有_id 上的默认索引和我在id 上添加的索引,这是类别ID 的数字表示。
  • 尝试一下,如果您过滤掉数据中的特定字段,它会突然快速加载吗?也许有一些奇怪的转换节点正在阻塞。
  • 你的文档是超级嵌套的吗?
  • 它非常嵌套,有几个深度。我喜欢你的想法,我会试试看。

标签: node.js performance mongodb profiling mongojs


【解决方案1】:

我的猜测是 .limit(1) 被忽略了,因为回调是提前提供的。一旦find 看到回调,它将执行查询,并且只有在将查询发送到 mongo 之后,.limit 修饰符才会尝试调整查询,但为时已晚。这样重新编码,看看是否可以解决:

db.collection('categories').find({ id: category_id }).limit(1).exec(
  function(err, docs) {
    callback(err, docs.length ? docs[0] : false);
});

【讨论】:

  • 这似乎没有任何区别。我也试过findOne 应该有同样的效果。查询速度非常快,就像在 NodeJS 之外一样,所以我猜问题出在 NodeJS 内部的某个地方。
【解决方案2】:

您很可能需要在对象中包含规范化和非规范化数据的组合。一次通过网络发送 4MB 似乎相当繁重,并且可能会导致任何将要解析数据的浏览器出现问题。

您很可能应该将前 100 种产品、产品的第一页或对您的应用程序有意义的一些较小的子集存储在该类别中。这可能是您确定的按字母顺序排列、最受欢迎、最新或其他一些特定于应用的指标。​​

当您开始编辑类别时,您将使用 $push/$slice 方法来确保避免数组无限制增长。

然后,当您实际对结果进行分页时,您将按类别对单个产品表进行单独查询。 (索引。)

我之前在这里写过这个: https://stackoverflow.com/a/27286612/68567

【讨论】:

  • 说实话,我已经照顾好浏览器了。 4MB gzipped 只有~300KB,我有一种方法可以进一步压缩它。浏览器不是这里的问题。到目前为止,TTFB 比下载响应所需的时间长!在进行了一些分析之后,我发现从我启动查询的那一刻到我从 MongoDB 模块得到响应的那一刻,一个 1 毫秒的查询有时可能需要长达 2 秒的时间。在我考虑解决我的问题的不同解决方案之前,我想了解它为什么会发生,是什么导致了这种延迟,特别是在 NodeJS 上。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-27
  • 1970-01-01
  • 2014-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多