MongoDB查询计划未使用复合索引

问题描述

我正在尝试使用有关公司简介保证金的数据集的MongoDB,以进行学习。这是示例文档

{
    "parent_comp" : 1
    "child_comp" : 101
    "profit" : NumberLong(70320020)
}

我创建了两个索引,即一个在child_comp字段上,另一个是具有parent_comp,child_comp和last_outage_timestamp的复合索引。

对于以下查询,我执行了 explain 命令以查看查询计划。

  MongoDB Enterprise > db.data.find({ "$and" : [{ "parent_comp" : 951,"child_comp" : 9351,"profit" : { "$gte" : { "$numberLong" : "500000000" } } },{ "profit" : { "$lte" : { "$numberLong" : "1000000000" } } }] }).sort({"profit" : 1}).limit(3).explain();
{
    "queryPlanner" : {
        "plannerVersion" : 1,"namespace" : "test.data","indexFilterSet" : false,"parsedQuery" : {
            "$and" : [
                {
                    "child_comp" : {
                        "$eq" : 9351
                    }
                },{
                    "parent_comp" : {
                        "$eq" : 951
                    }
                },{
                    "profit" : {
                        "$lte" : {
                            "$numberLong" : "1000000000"
                        }
                    }
                },{
                    "profit" : {
                        "$gte" : {
                            "$numberLong" : "500000000"
                        }
                    }
                }
            ]
        },"queryHash" : "B570EF0C","planCacheKey" : "187EF74B","winningPlan" : {
            "stage" : "LIMIT","limitAmount" : 3,"inputStage" : {
                "stage" : "FETCH","filter" : {
                    "$and" : [
                        {
                            "child_comp" : {
                                "$eq" : 9351
                            }
                        },{
                            "parent_comp" : {
                                "$eq" : 951
                            }
                        }
                    ]
                },"inputStage" : {
                    "stage" : "IXSCAN","keyPattern" : {
                        "profit" : 1
                    },"indexName" : "profit_index","ismultikey" : false,"multikeyPaths" : {
                        "profit" : [ ]
                    },"isUnique" : false,"issparse" : false,"isPartial" : false,"indexVersion" : 2,"direction" : "forward","indexBounds" : {
                        "profit" : [
                            "[{ $numberLong: \"500000000\" },{ $numberLong: \"1000000000\" }]"
                        ]
                    }
                }
            }
        },"rejectedplans" : [
            {
                "stage" : "SORT","sortPattern" : {
                    "profit" : 1
                },"inputStage" : {
                    "stage" : "SORT_KEY_GENERATOR","inputStage" : {
                        "stage" : "FETCH","filter" : {
                            "$and" : [
                                {
                                    "parent_comp" : {
                                        "$eq" : 951
                                    }
                                },{
                                    "profit" : {
                                        "$lte" : {
                                            "$numberLong" : "1000000000"
                                        }
                                    }
                                },{
                                    "profit" : {
                                        "$gte" : {
                                            "$numberLong" : "500000000"
                                        }
                                    }
                                }
                            ]
                        },"inputStage" : {
                            "stage" : "IXSCAN","keyPattern" : {
                                "child_comp" : 1
                            },"indexName" : "child_comp_index","multikeyPaths" : {
                                "child_comp" : [ ]
                            },"indexBounds" : {
                                "child_comp" : [
                                    "[9351.0,9351.0]"
                                ]
                            }
                        }
                    }
                }
            },{
                "stage" : "LIMIT","inputStage" : {
                    "stage" : "FETCH","inputStage" : {
                        "stage" : "IXSCAN","keyPattern" : {
                            "parent_comp" : 1,"child_comp" : 1,"profit" : 1
                        },"indexName" : "parent_comp_1_child_comp_1_profit_1","multikeyPaths" : {
                            "parent_comp" : [ ],"child_comp" : [ ],"profit" : [ ]
                        },"indexBounds" : {
                            "parent_comp" : [
                                "[951.0,951.0]"
                            ],"child_comp" : [
                                "[9351.0,9351.0]"
                            ],"profit" : [
                                "[{ $numberLong: \"500000000\" },{ $numberLong: \"1000000000\" }]"
                            ]
                        }
                    }
                }
            }
        ]
    },"serverInfo" : {
        "host" : "localhost","port" : 27017,"version" : "4.2.8","gitVersion" : "43d25888249164d76d5e04dd6cf38f6111e21f5f"
    },"ok" : 1
}

如您所见,获胜计划使用单一索引而不是复合索引。所以,请您让我知道为什么不使用复合索引。

解决方法

您的查询是按利润排序的,并且复合索引不包括您要进行排序的字段,因此使用复合索引将需要一个附加的排序阶段。

权衡和推理将在in the docs中进一步说明。

另请参阅https://www.alexbevi.com/blog/2020/05/16/optimizing-mongodb-compound-indexes-the-equality-sort-range-esr-rule/