【问题标题】:MongoDB Aggregation Query Optimization : match -> unwind -> match vs unwind->matchMongoDB聚合查询优化:match -> unwind -> match vs unwind->match
【发布时间】:2020-08-06 10:40:10
【问题描述】:

输入数据

{
    "_id" : ObjectId("5dc7ac6e720a2772c7b76671"),
    "idList" : [ 
        {
            "queueUpdateTimeStamp" : "2019-12-12T07:16:47.577Z",
            "displayId" : "H14",
            "currentQueue" : "10",
            "isRejected" : true,
            "isDispacthed" : true
        },
        {
            "queueUpdateTimeStamp" : "2019-12-12T07:16:47.577Z",
            "displayId" : "H14",
            "currentQueue" : "10",
            "isRejected" : true,
            "isDispacthed" : false
        }
    ],
    "poDetailsId" : ObjectId("5dc7ac15720a2772c7b7666f"),
    "processtype" : 1
}

输出数据

{
    "_id" : ObjectId("5dc7ac6e720a2772c7b76671"),
    "idList":
     {
            "queueUpdateTimeStamp" : "2019-12-12T07:16:47.577Z",
            "displayId" : "H14",
            "currentQueue" : "10",
            "isRejected" : true,
            "isDispacthed" : true
    },
    "poDetailsId" : ObjectId("5dc7ac15720a2772c7b7666f"),
    "processtype" : 1
}

查询 1(unwind 然后match

     aggregate([
     {
         $unwind: { path: "$idList" }
     },
     {
         $match: { 'idList.isDispacthed': isDispatched }
     }
     ])

查询 2(match 然后unwind 然后match

     aggregate([
     {
         $match: { 'idList.isDispacthed': isDispatched }
     },
     {
         $unwind: { path: "$idList" }
     },
     {
         $match: { 'idList.isDispacthed': isDispatched }
     }
     ])

我的问题/我的担忧

(假设我在此集合中有大量文档(50k +),并假设我在同一管道中的此查询之后还有其他查找和预测)

match -> unwind -> match VS unwind ->match

  1. 这两个查询之间是否存在性能差异?
  2. 还有其他(更好的)方法来编写这个查询吗?

【问题讨论】:

    标签: mongodb mongoose mongodb-query aggregation-framework aggregation


    【解决方案1】:

    这一切都取决于 MongoDB 查询计划器优化器:

    聚合管道操作有一个优化阶段,它试图重塑管道以提高性能。

    要查看优化器如何转换特定聚合管道,请在 db.collection.aggregate() 方法中包含 explain 选项。

    https://docs.mongodb.com/manual/core/aggregation-pipeline-optimization/

    poDetailsId 创建索引并运行此查询:

    db.getCollection('collection').explain().aggregate([
         {
             $unwind: "$idList"
         },
          {
             $match: { 
               'idList.isDispacthed': true, 
               "poDetailsId" : ObjectId("5dc7ac15720a2772c7b7666f") 
             }
         }  
    ])
    

    {
        "stages" : [ 
            {
                "$cursor" : {
                    "query" : {
                        "poDetailsId" : {
                            "$eq" : ObjectId("5dc7ac15720a2772c7b7666f")
                        }
                    },
                    "queryPlanner" : {
                        "plannerVersion" : 1,
                        "namespace" : "test.collection",
                        "indexFilterSet" : false,
                        "parsedQuery" : {
                            "poDetailsId" : {
                                "$eq" : ObjectId("5dc7ac15720a2772c7b7666f")
                            }
                        },
                        "queryHash" : "2CF7E390",
                        "planCacheKey" : "A8739F51",
                        "winningPlan" : {
                            "stage" : "FETCH",
                            "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                    "poDetailsId" : 1.0
                                },
                                "indexName" : "poDetailsId_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                    "poDetailsId" : []
                                },
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                    "poDetailsId" : [ 
                                        "[ObjectId('5dc7ac15720a2772c7b7666f'), ObjectId('5dc7ac15720a2772c7b7666f')]"
                                    ]
                                }
                            }
                        },
                        "rejectedPlans" : []
                    }
                }
            }, 
            {
                "$unwind" : {
                    "path" : "$idList"
                }
            }, 
            {
                "$match" : {
                    "idList.isDispacthed" : {
                        "$eq" : true
                    }
                }
            }
        ],
        "ok" : 1.0
    }
    

    如您所见,MongoDB 会将这个聚合更改为:

    db.getCollection('collection').aggregate([
         {
             $match: { "poDetailsId" : ObjectId("5dc7ac15720a2772c7b7666f") }
         }
         {
             $unwind: "$idList"
         },
         {
             $match: { 'idList.isDispacthed': true }
         }  
    ])
    

    从逻辑上讲,$match -> $unwind -> $match 更好,因为您过滤(按索引)记录子集而不是完全扫描(处理 100 个匹配的文档≠所有文档)。

    如果您的聚合操作只需要集合中的数据子集,请使用$match$limit$skip 阶段来限制在管道开头输入的文档。当放置在管道的开头时,$match 操作使用合适的索引仅扫描集合中的匹配文档

    https://docs.mongodb.com/manual/core/aggregation-pipeline/#early-filtering

    一旦您操作了您的文档,MongoDB 就无法应用索引。

    【讨论】:

    • Aggregation pipeline operations have an optimization phase which attempts to reshape the pipeline for improved performance 你能解释一下吗?请 。或提供链接以了解这一点。这不意味着unwind-> match 会变成match->unwind ????
    • @kumarkundan 执行以下操作:db.collection.explain().aggregate([...]) 它将显示 MongoDB 将执行哪种聚合查询。
    • @kumarkundan 我已经更新了我的答案,请再检查一遍
    猜你喜欢
    • 1970-01-01
    • 2021-05-04
    • 1970-01-01
    • 1970-01-01
    • 2020-07-09
    • 2019-05-30
    • 1970-01-01
    • 2017-08-27
    • 2020-09-22
    相关资源
    最近更新 更多