Mongodb 了解explain()以优化复杂查询

Mongodb 了解explain()以优化复杂查询,mongodb,indexing,lookup,explain,Mongodb,Indexing,Lookup,Explain,我有一个聚合管道,需要5秒钟才能返回200行。 我正试图使用explain(“executionStats”)优化我当前的管道 这是我的管道: db.getCollection("content_topics").explain("executionStats").aggregate([{ "$lookup": { from: "users", localField: "creator", foreignField: "_id", as:

我有一个聚合管道,需要5秒钟才能返回200行。 我正试图使用explain(“executionStats”)优化我当前的管道

这是我的管道:

db.getCollection("content_topics").explain("executionStats").aggregate([{
    "$lookup": {
      from: "users",
      localField: "creator",
      foreignField: "_id",
      as: "user"
    }
  },
  {
    $unwind: "$user"
  },
  {
    "$match": {
      created_at: {
        "$gte": 1528914600,
        "$lte": 1534271400
      },
      dash_status: 3,
      language: "hi",
      parent_topic_id: {
        "$eq": null
      },
      status: 1,
      "user.device_os": {
        "$ne": "BOT"
      }
    }
  },
  {
    "$sort": {
      created_at: -1
    }
  },
  {
    "$addFields": {
      user_handle: "$user.handle",
      user_phone: "$user.phone",
      user_status: "$user.status"
    }
  },
  {
    "$project": {
      topic_id: 1,
      n_vokes: 1,
      message: 1,
      title: 1,
      language: 1,
      description: 1,
      voice_desc: 1,
      image: 1,
      image_share: 1,
      hashtag: 1,
      location: 1,
      default_text: 1,
      creator: 1,
      created_at: 1,
      status: 1,
      ref_id: 1,
      weightage: 1,
      username: 1,
      slug_generated: 1,
      user_handle: 1,
      user_phone: 1,
      user_status: 1
    }
  },
  {
    "$skip": 0
  },
  {
    "$limit": 200
  }
]);
这就是mongo解释的内容:

这是在我使用“$match”中使用的键创建了索引“index\u for_dashboard”之后。我还从用户集合中为“device_os”创建了一个索引。但是没有骰子,响应时间没有改善

可能的罪犯:

  • 美元很贵。如果是,我可以只取我需要的字段
  • 索引可以做得更好吗。我应该使用另一套吗 田地
  • addField美元贵吗。如果是的话,我可以把它卸到 应用程序级别
我应该如何对解释结果进行故障排除(并理解),以帮助自己优化查询?
我不能在这里打电话,需要一些指导。

首先,您需要确保至少运行MongoDB的v3.6.3,因为在v3.6中有一个新功能,允许指定子管道。这些管道实际上可以使用索引,但是,只有在上面提到的版本中才修复了一个问题

以下内容应尽可能快:

db.getCollection("content_topics").createIndex({ created_at: -1, dash_status: 1, language: 1, parent_topic_id: 1, status: 1 }); // this index will get used by the main $match and the $sort stage

db.getCollection("users").createIndex({ device_os: 1 }); // this index will get used by the sub-pipeline in $lookup

db.getCollection("content_topics").aggregate([{
    "$match": { // filter at the start in order to be able to use indexes
      created_at: {
        "$gte": 1528914600,
        "$lte": 1534271400
      },
      dash_status: 3,
      language: "hi",
      parent_topic_id: {
        "$eq": null
      },
      status: 1
      // see the below $lookup stage in case you're wondering where the user filter went
    }
  },
  {
    "$sort": {
      created_at: -1 // sort straight away so the index can be used
    }
  },
  {
    "$lookup": {
      from: "users",
      let: { "creator": "$creator" },
      pipeline: [{ // use new v3.6 pipeline syntax to be able to leverage indexes
          $match: {
              $expr: {
                  $and: [
                      { $eq: [ "$_id", "$$creator" ] },
                      { $ne: [ "$device_os", "BOT" ] } // here is the "device_os" filter inside the pipeline so index can be used
                  ]
              }
          }
      }, {
          $project: {
              _id: 0, // "_id" field is not needed
              user_handle: 1, // only those fields are of interest
              user_phone: 1,
              user_status: 1
          }
      }],
      as: "user"
    }
  },
  {
    $unwind: "$user"
  },
  {
    "$project": {
      topic_id: 1,
      n_vokes: 1,
      message: 1,
      title: 1,
      language: 1,
      description: 1,
      voice_desc: 1,
      image: 1,
      image_share: 1,
      hashtag: 1,
      location: 1,
      default_text: 1,
      creator: 1,
      created_at: 1,
      status: 1,
      ref_id: 1,
      weightage: 1,
      username: 1,
      slug_generated: 1,
      user_handle: "$user.handle", // no extra $addFields stage needed
      user_phone: "$user.phone",   // same here
      user_status: "$user.status"  // and here
    }
  },
  {
    "$skip": 0
  },
  {
    "$limit": 200
  }
]);

首先,您需要确保至少运行MongoDB的v3.6.3,因为在v3.6中有一个新特性,允许指定子管道。这些管道实际上可以使用索引,但是,只有在上面提到的版本中才修复了一个问题

以下内容应尽可能快:

db.getCollection("content_topics").createIndex({ created_at: -1, dash_status: 1, language: 1, parent_topic_id: 1, status: 1 }); // this index will get used by the main $match and the $sort stage

db.getCollection("users").createIndex({ device_os: 1 }); // this index will get used by the sub-pipeline in $lookup

db.getCollection("content_topics").aggregate([{
    "$match": { // filter at the start in order to be able to use indexes
      created_at: {
        "$gte": 1528914600,
        "$lte": 1534271400
      },
      dash_status: 3,
      language: "hi",
      parent_topic_id: {
        "$eq": null
      },
      status: 1
      // see the below $lookup stage in case you're wondering where the user filter went
    }
  },
  {
    "$sort": {
      created_at: -1 // sort straight away so the index can be used
    }
  },
  {
    "$lookup": {
      from: "users",
      let: { "creator": "$creator" },
      pipeline: [{ // use new v3.6 pipeline syntax to be able to leverage indexes
          $match: {
              $expr: {
                  $and: [
                      { $eq: [ "$_id", "$$creator" ] },
                      { $ne: [ "$device_os", "BOT" ] } // here is the "device_os" filter inside the pipeline so index can be used
                  ]
              }
          }
      }, {
          $project: {
              _id: 0, // "_id" field is not needed
              user_handle: 1, // only those fields are of interest
              user_phone: 1,
              user_status: 1
          }
      }],
      as: "user"
    }
  },
  {
    $unwind: "$user"
  },
  {
    "$project": {
      topic_id: 1,
      n_vokes: 1,
      message: 1,
      title: 1,
      language: 1,
      description: 1,
      voice_desc: 1,
      image: 1,
      image_share: 1,
      hashtag: 1,
      location: 1,
      default_text: 1,
      creator: 1,
      created_at: 1,
      status: 1,
      ref_id: 1,
      weightage: 1,
      username: 1,
      slug_generated: 1,
      user_handle: "$user.handle", // no extra $addFields stage needed
      user_phone: "$user.phone",   // same here
      user_status: "$user.status"  // and here
    }
  },
  {
    "$skip": 0
  },
  {
    "$limit": 200
  }
]);

感谢您花时间详细解释这一点。我完全不知道我们可以在$lookup中使用管道,这非常漂亮,可以让我在查找之前放置匹配项。此外,它还有助于缩短响应时间,这可以通过
nReturned
/
works
将响应时间从7k降低到2k来确认:感谢您花时间详细解释这一点。我完全不知道我们可以在$lookup中使用管道,这非常漂亮,可以让我在查找之前放置匹配项。此外,它还有助于缩短响应时间,这可以通过
nReturned
/
works
将响应时间从7k降低到2k来确认: