【问题标题】:Mongo find query takes 2 minutesMongo 查找查询需要 2 分钟
【发布时间】:2017-05-16 08:45:48
【问题描述】:

我的集合中有大约 75,000 个文档。

数据库的总大小约为 45GB。
在 75k 文档中,大约 45k 每个 900 KB(大约 42 GB),其余文档每个大约 120 KB。

每个文档都映射到其他集合中的一个custId ObjectId,并有一个timestamp,两者都被索引。

现在我需要获取上个月特定custId 的文档。 数量约为 5500 个文档。这个custId 包含每个大小约为 120 KB 的小文档。

以下是我的查询:

db.mycollection.find(
{
    custId:ObjectId("CUST_OBJECT_ID_HERE"),
    timestamp:{$gte:one_month_ago_date, $lt:current_date}
}).sort({timestamp:-1})

查询仍然需要 2 分钟才能获取所有记录。是因为文档数量还是较大文档的大小?有什么办法可以解决这个问题吗?

注意: 从 nodejs 触发查询需要 2 分钟。如果我在 mongo shell 上触发它,它会快速返回,但可能是因为它只是获取前 50 条记录。当我将.count() 附加到 mongo shell 上的查询时,需要 2 分钟才能返回计数。

更新:
索引详情:

"wiredTiger" : {
    "nindexes" : 3,
    "totalIndexSize" : 2396160,
    "indexSizes" : {
        "_id_" : 1138688,
        "custId_1" : 598016,
        "timestamp_1" : 659456
    }
}

解释输出:(带排序)

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "mydb.mycollection",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "$and" : [
                {
                    "custId" : {
                        "$eq" : ObjectId("CUST_OBJECT_ID_HERE")
                    }
                },
                {
                    "timestamp" : {
                        "$lt" : ISODate("2017-05-15T14:20:04.393Z")
                    }
                },
                {
                    "timestamp" : {
                        "$gte" : ISODate("2017-04-15T14:20:04.393Z")
                    }
                }
            ]
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "filter" : {
                "custId" : {
                    "$eq" : ObjectId("CUST_OBJECT_ID_HERE")
                }
            },
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "timestamp" : 1
                },
                "indexName" : "timestamp_1",
                "isMultiKey" : false,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 1,
                "direction" : "backward",
                "indexBounds" : {
                    "timestamp" : [
                        "(new Date(1494858004393), new Date(1492266004393)]"
                    ]
                }
            }
        },
        "rejectedPlans" : [
            {
                "stage" : "SORT",
                "sortPattern" : {
                    "timestamp" : -1
                },
                "inputStage" : {
                    "stage" : "SORT_KEY_GENERATOR",
                    "inputStage" : {
                        "stage" : "FETCH",
                        "filter" : {
                            "$and" : [
                                {
                                    "timestamp" : {
                                        "$lt" : ISODate("2017-05-15T14:20:04.393Z")
                                    }
                                },
                                {
                                    "timestamp" : {
                                        "$gte" : ISODate("2017-04-15T14:20:04.393Z")
                                    }
                                }
                            ]
                        },
                        "inputStage" : {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "custId" : 1
                            },
                            "indexName" : "custId_1",
                            "isMultiKey" : false,
                            "isUnique" : false,
                            "isSparse" : false,
                            "isPartial" : false,
                            "indexVersion" : 1,
                            "direction" : "forward",
                            "indexBounds" : {
                                "custId" : [
                                    "[ObjectId('CUST_OBJECT_ID_HERE'), ObjectId('CUST_OBJECT_ID_HERE')]"
                                ]
                            }
                        }
                    }
                }
            }
        ]
    },
    "serverInfo" : {
        "host" : "test-machine",
        "port" : 27017,
        "version" : "3.2.12",
        "gitVersion" : "REMOVED_BY_OP"
    },
    "ok" : 1
}

【问题讨论】:

  • 这是因为排序。它必须将整个内容加载到内存或硬盘驱动器中。由于数据库的大小,它可能会加载到很慢的硬盘驱动器上。您可以尝试仅指定您需要检索的属性,这将使其更轻,并可能使其适合内存。我认为在使用排序时它也只会使用 1 个索引,它会选择时间戳索引并忽略 custId 索引。您可以尝试为 custId 和时间戳添加复合索引。
  • 如果您不需要所有文档,也可以使用分页
  • 你对这个集合有什么索引?你试过mongodb解释吗?如果在 mongo shell 中没有排序,这个查询如何执行?
  • @Astro,如前所述,custId 和 timestamp 已编入索引。没有排序需要2分钟。使用排序会增加 29 秒。
  • @Love-Kesh,不,我需要所有文档。

标签: node.js mongodb mongodb-query node-mongodb-native


【解决方案1】:

这就是索引的用途!

为时间戳和 custId 创建索引(两者的复合索引将是最有效的),你就可以了。由于按时间戳排序,在复合索引中,将时间戳设为第一个(顺序很重要)


这是在 mongo 中创建复合索引的代码:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const userSchema = new Schema({
    //...
});

userSchema.index({timestamp: 1, custId: 1});

mongoose.model('User', userSchema);
module.exports = userSchema;

【讨论】:

  • 你能详细说明一下复合索引吗?如前所述,我已经索引了custId 和timestamp,但不是复合索引。如果你能提到如何用猫鼬来完成,那就更好了。
  • @DushyantBangal 请参考@Astro answear 创建复合(多个键的组合)索引。请查看化合物索引的 Mongoose 文档:https://docs.mongodb.com/manual/indexes/#Indexes-CompoundKeys
  • @DushyantBangal - 复合索引是一种同时映射更多字段的索引。基本上它是最好的选择,如果你想快速选择查询中包含的所有这些字段。内部功能并不是那么容易,但只要您不使用多个字段对其进行排序并且始终使用查询中的所有字段进行搜索,它就可以正常工作。
【解决方案2】:

试试这个索引:

db.mycollection.createIndex({custId:1,timestamp:1}, {background:true})

【讨论】:

    【解决方案3】:

    上面的答案都是完全正确的。只需投入我的 2 美分。这个答案很大程度上取决于您可用的内存,以及您需要返回的信息是“实时”还是可以以某种方式缓存信息。

    Mongodb 因内存使用而臭名昭著。 (我喜欢 mongodb,但内存是致命弱点)。其次,如上所述,您可以采取任何措施来改善查询结果在进行查询之前,在时间、读取和核心使用方面都是一个很大的优势。当涉及到文档存储时,您可能(或将会)找到正确设置的 Redis 缓存也将极大地帮助您降低响应时间。

    显然,这需要内存,在您的情况下需要平衡(包括负载平衡)。它是内存、速度和磁盘使用(即使是 SSD)的适当组合,这将帮助您根据系统要求平衡这些查询请求。

    希望能有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-05
      • 2018-07-20
      • 1970-01-01
      • 1970-01-01
      • 2019-02-18
      • 2014-01-01
      • 2018-02-13
      • 1970-01-01
      相关资源
      最近更新 更多