MongoDB - 排序和限制范围查询时未使用索引

Swa*_*p56 2 mongodb morphia mongodb-query

我正在尝试对包含公告板数据的集合使用范围查询来获取项目的排序列表。一个“线程”文档的数据结构是:

{
    "_id" : ObjectId("5a779b47f4fa72412126526a"),
    "title" : "necessitatibus tincidunt libris assueverit",
    "content" : "Corrumpitvenenatis cubilia adipiscing sollicitudin",
    "flagged" : false,
    "locked" : false,
    "sticky" : false,
    "lastPostAt" : ISODate("2018-02-05T06:35:24.656Z"),
    "postCount" : 42,
    "user" : ObjectId("5a779b46f4fa72412126525a"),
    "category" : ObjectId("5a779b31f4fa724121265164"),
    "createdAt" : ISODate("2018-02-04T23:46:15.852Z"),
    "updatedAt" : ISODate("2018-02-05T06:35:24.656Z")
}
Run Code Online (Sandbox Code Playgroud)

查询是:

db.threads.find({
    category: ObjectId('5a779b31f4fa724121265142'), 
    _id : { $gt: ObjectId('5a779b5cf4fa724121269be8') }
}).sort({ sticky: -1, lastPostAt: -1, _id: 1 }).limit(25)
Run Code Online (Sandbox Code Playgroud)

我设置了以下索引来支持它:

{ category: 1, _id: 1 }
{ category: 1, _id: 1, sticky: 1, lastPostAt: 1 }
{ sticky: 1, lastPostAt: 1, _id: 1 }
Run Code Online (Sandbox Code Playgroud)

尽管如此,它仍然根据执行统计扫描数百个文档/密钥:

{
    "executionStats" : {
    "executionSuccess" : true,
    "nReturned" : 772,
    "executionTimeMillis" : 17,
    "totalKeysExamined" : 772,
    "totalDocsExamined" : 772,
    "executionStages" : {
        "stage" : "SORT",
        "nReturned" : 772,
        "executionTimeMillisEstimate" : 0,
        "works" : 1547,
        "advanced" : 772,
        "needTime" : 774,
        "needYield" : 0,
        "saveState" : 33,
        "restoreState" : 33,
        "isEOF" : 1,
        "invalidates" : 0,
        "sortPattern" : {
            "sticky" : -1,
            "lastPostAt" : -1,
            "_id" : 1
        },
        "memUsage" : 1482601,
        "memLimit" : 33554432,
        "inputStage" : {
            "stage" : "SORT_KEY_GENERATOR",
            "nReturned" : 772,
            "executionTimeMillisEstimate" : 0,
            "works" : 774,
            "advanced" : 772,
            "needTime" : 1,
            "needYield" : 0,
            "saveState" : 33,
            "restoreState" : 33,
            "isEOF" : 1,
            "invalidates" : 0,
            "inputStage" : {
                "stage" : "FETCH",
                "nReturned" : 772,
                "executionTimeMillisEstimate" : 0,
                "works" : 773,
                "advanced" : 772,
                "needTime" : 0,
                "needYield" : 0,
                "saveState" : 33,
                "restoreState" : 33,
                "isEOF" : 1,
                "invalidates" : 0,
                "docsExamined" : 772,
                "alreadyHasObj" : 0,
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "nReturned" : 772,
                    "executionTimeMillisEstimate" : 0,
                    "works" : 773,
                    "advanced" : 772,
                    "needTime" : 0,
                    "needYield" : 0,
                    "saveState" : 33,
                    "restoreState" : 33,
                    "isEOF" : 1,
                    "invalidates" : 0,
                    "keyPattern" : {
                        "category" : 1,
                        "_id" : 1,
                        "sticky" : 1,
                        "lastPostAt" : 1
                    },
                    "indexName" : "category_1__id_1_sticky_1_lastPostAt_1",
                    "isMultiKey" : false,
                    "multiKeyPaths" : {
                        "category" : [ ],
                        "_id" : [ ],
                        "sticky" : [ ],
                        "lastPostAt" : [ ]
                    },
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 2,
                    "direction" : "forward",
                    "indexBounds" : {
                        "category" : [
                            "[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
                        ],
                        "_id" : [
                            "(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
                        ],
                        "sticky" : [
                            "[MinKey, MaxKey]"
                        ],
                        "lastPostAt" : [
                            "[MinKey, MaxKey]"
                        ]
                    },
                    "keysExamined" : 772,
                    "seeks" : 1,
                    "dupsTested" : 0,
                    "dupsDropped" : 0,
                    "seenInvalidated" : 0
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当我取出排序台时,它只能正确扫描 25 个文档。无论我在排序函数中放置哪些字段,检查的键 (772) 都保持不变。

以下是已explain()排序查询的完整内容:

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "database.threads",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "$and" : [
                {
                    "category" : {
                        "$eq" : ObjectId("5a779b31f4fa724121265142")
                    }
                },
                {
                    "_id" : {
                        "$gt" : ObjectId("5a779b5cf4fa724121269be8")
                    }
                }
            ]
        },
        "winningPlan" : {
            "stage" : "SORT",
            "sortPattern" : {
                "sticky" : -1,
                "lastPostAt" : -1,
                "_id" : 1
            },
            "limitAmount" : 25,
            "inputStage" : {
                "stage" : "SORT_KEY_GENERATOR",
                "inputStage" : {
                    "stage" : "FETCH",
                    "inputStage" : {
                        "stage" : "IXSCAN",
                        "keyPattern" : {
                            "category" : 1,
                            "_id" : 1,
                            "sticky" : 1,
                            "lastPostAt" : 1
                        },
                        "indexName" : "category_1__id_1_sticky_1_lastPostAt_1",
                        "isMultiKey" : false,
                        "multiKeyPaths" : {
                            "category" : [ ],
                            "_id" : [ ],
                            "sticky" : [ ],
                            "lastPostAt" : [ ]
                        },
                        "isUnique" : false,
                        "isSparse" : false,
                        "isPartial" : false,
                        "indexVersion" : 2,
                        "direction" : "forward",
                        "indexBounds" : {
                            "category" : [
                                "[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
                            ],
                            "_id" : [
                                "(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
                            ],
                            "sticky" : [
                                "[MinKey, MaxKey]"
                            ],
                            "lastPostAt" : [
                                "[MinKey, MaxKey]"
                            ]
                        }
                    }
                }
            }
        },
        "rejectedPlans" : [
            {
                "stage" : "SORT",
                "sortPattern" : {
                    "sticky" : -1,
                    "lastPostAt" : -1,
                    "_id" : 1
                },
                "limitAmount" : 25,
                "inputStage" : {
                    "stage" : "SORT_KEY_GENERATOR",
                    "inputStage" : {
                        "stage" : "FETCH",
                        "filter" : {
                            "_id" : {
                                "$gt" : ObjectId("5a779b5cf4fa724121269be8")
                            }
                        },
                        "inputStage" : {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "category" : 1
                            },
                            "indexName" : "category_1",
                            "isMultiKey" : false,
                            "multiKeyPaths" : {
                                "category" : [ ]
                            },
                            "isUnique" : false,
                            "isSparse" : false,
                            "isPartial" : false,
                            "indexVersion" : 2,
                            "direction" : "forward",
                            "indexBounds" : {
                                "category" : [
                                    "[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
                                ]
                            }
                        }
                    }
                }
            },
            {
                "stage" : "SORT",
                "sortPattern" : {
                    "sticky" : -1,
                    "lastPostAt" : -1,
                    "_id" : 1
                },
                "limitAmount" : 25,
                "inputStage" : {
                    "stage" : "SORT_KEY_GENERATOR",
                    "inputStage" : {
                        "stage" : "FETCH",
                        "inputStage" : {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "category" : 1,
                                "_id" : 1
                            },
                            "indexName" : "category_1__id_1",
                            "isMultiKey" : false,
                            "multiKeyPaths" : {
                                "category" : [ ],
                                "_id" : [ ]
                            },
                            "isUnique" : false,
                            "isSparse" : false,
                            "isPartial" : false,
                            "indexVersion" : 2,
                            "direction" : "forward",
                            "indexBounds" : {
                                "category" : [
                                    "[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
                                ],
                                "_id" : [
                                    "(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
                                ]
                            }
                        }
                    }
                }
            },
            {
                "stage" : "SORT",
                "sortPattern" : {
                    "sticky" : -1,
                    "lastPostAt" : -1,
                    "_id" : 1
                },
                "limitAmount" : 25,
                "inputStage" : {
                    "stage" : "SORT_KEY_GENERATOR",
                    "inputStage" : {
                        "stage" : "FETCH",
                        "filter" : {
                            "category" : {
                                "$eq" : ObjectId("5a779b31f4fa724121265142")
                            }
                        },
                        "inputStage" : {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "_id" : 1
                            },
                            "indexName" : "_id_",
                            "isMultiKey" : false,
                            "multiKeyPaths" : {
                                "_id" : [ ]
                            },
                            "isUnique" : true,
                            "isSparse" : false,
                            "isPartial" : false,
                            "indexVersion" : 2,
                            "direction" : "forward",
                            "indexBounds" : {
                                "_id" : [
                                    "(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
                                ]
                            }
                        }
                    }
                }
            }
        ]
    },
    "serverInfo" : {
        "host" : "CRF-MBP.local",
        "port" : 27017,
        "version" : "3.6.2",
        "gitVersion" : "489d177dbd0f0420a8ca04d39fd78d0a2c539420"
    },
    "ok" : 1
}
Run Code Online (Sandbox Code Playgroud)

explain()是非排序查询的完整内容:

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "database.threads",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "$and" : [
                {
                    "category" : {
                        "$eq" : ObjectId("5a779b31f4fa724121265142")
                    }
                },
                {
                    "_id" : {
                        "$gt" : ObjectId("5a779b5cf4fa724121269be8")
                    }
                }
            ]
        },
        "winningPlan" : {
            "stage" : "LIMIT",
            "limitAmount" : 25,
            "inputStage" : {
                "stage" : "FETCH",
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "category" : 1,
                        "_id" : 1,
                        "sticky" : 1,
                        "lastPostAt" : 1
                    },
                    "indexName" : "category_1__id_1_sticky_1_lastPostAt_1",
                    "isMultiKey" : false,
                    "multiKeyPaths" : {
                        "category" : [ ],
                        "_id" : [ ],
                        "sticky" : [ ],
                        "lastPostAt" : [ ]
                    },
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 2,
                    "direction" : "forward",
                    "indexBounds" : {
                        "category" : [
                            "[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
                        ],
                        "_id" : [
                            "(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
                        ],
                        "sticky" : [
                            "[MinKey, MaxKey]"
                        ],
                        "lastPostAt" : [
                            "[MinKey, MaxKey]"
                        ]
                    }
                }
            }
        },
        "rejectedPlans" : [
            {
                "stage" : "LIMIT",
                "limitAmount" : 25,
                "inputStage" : {
                    "stage" : "FETCH",
                    "filter" : {
                        "_id" : {
                            "$gt" : ObjectId("5a779b5cf4fa724121269be8")
                        }
                    },
                    "inputStage" : {
                        "stage" : "IXSCAN",
                        "keyPattern" : {
                            "category" : 1
                        },
                        "indexName" : "category_1",
                        "isMultiKey" : false,
                        "multiKeyPaths" : {
                            "category" : [ ]
                        },
                        "isUnique" : false,
                        "isSparse" : false,
                        "isPartial" : false,
                        "indexVersion" : 2,
                        "direction" : "forward",
                        "indexBounds" : {
                            "category" : [
                                "[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
                            ]
                        }
                    }
                }
            },
            {
                "stage" : "LIMIT",
                "limitAmount" : 25,
                "inputStage" : {
                    "stage" : "FETCH",
                    "inputStage" : {
                        "stage" : "IXSCAN",
                        "keyPattern" : {
                            "category" : 1,
                            "_id" : 1
                        },
                        "indexName" : "category_1__id_1",
                        "isMultiKey" : false,
                        "multiKeyPaths" : {
                            "category" : [ ],
                            "_id" : [ ]
                        },
                        "isUnique" : false,
                        "isSparse" : false,
                        "isPartial" : false,
                        "indexVersion" : 2,
                        "direction" : "forward",
                        "indexBounds" : {
                            "category" : [
                                "[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
                            ],
                            "_id" : [
                                "(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
                            ]
                        }
                    }
                }
            },
            {
                "stage" : "LIMIT",
                "limitAmount" : 25,
                "inputStage" : {
                    "stage" : "FETCH",
                    "filter" : {
                        "category" : {
                            "$eq" : ObjectId("5a779b31f4fa724121265142")
                        }
                    },
                    "inputStage" : {
                        "stage" : "IXSCAN",
                        "keyPattern" : {
                            "_id" : 1
                        },
                        "indexName" : "_id_",
                        "isMultiKey" : false,
                        "multiKeyPaths" : {
                            "_id" : [ ]
                        },
                        "isUnique" : true,
                        "isSparse" : false,
                        "isPartial" : false,
                        "indexVersion" : 2,
                        "direction" : "forward",
                        "indexBounds" : {
                            "_id" : [
                                "(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
                            ]
                        }
                    }
                }
            }
        ]
    },
    "serverInfo" : {
        "host" : "CRF-MBP.local",
        "port" : 27017,
        "version" : "3.6.2",
        "gitVersion" : "489d177dbd0f0420a8ca04d39fd78d0a2c539420"
    },
    "ok" : 1
}
Run Code Online (Sandbox Code Playgroud)

有谁知道为什么这可能不完全使用索引?

kev*_*adi 9

问题是您的索引实际上都没有帮助排序查询。这就是大量扫描对象和存在SORT_KEY_GENERATOR阶段(内存中排序,限制为 32MB)的原因。

另一方面,非排序查询可以使用{ category: 1, _id: 1 }{ category: 1, _id: 1, sticky: 1, lastPostAt: 1 }索引。请注意,使用任何一个都是完全有效的,因为一个包含另一个的前缀。有关更多详细信息,请参阅前缀

MongoDBfind()查询通常仅使用一个索引,因此单个复合索引应满足查询的所有参数。这将包括两个参数find()sort()

Optimizing MongoDB Compound Indexes 中有一篇关于如何创建索引的好文章。让我们看一下文章的要点,复合索引的顺序应该是相等 --> 排序 --> 范围

您的查询“形状”是:

db.collection.find({category:..., _id: {$gt:...}})
             .sort({sticky:-1, lastPostAt:-1, _id:1})
             .limit(25)
Run Code Online (Sandbox Code Playgroud)

我们看到:

  • category:...平等
  • sticky:-1, lastPostAt:-1, _id:1排序
  • _id: {$gt:...}范围

所以你需要的复合索引是:

{category:1, sticky:-1, lastPostAt:-1, _id:1}
Run Code Online (Sandbox Code Playgroud)

explain()具有上述索引的查询输出的获胜计划显示:

"winningPlan": {
      "stage": "LIMIT",
      "limitAmount": 25,
      "inputStage": {
        "stage": "FETCH",
        "inputStage": {
          "stage": "IXSCAN",
          "keyPattern": {
            "category": 1,
            "sticky": -1,
            "lastPostAt": -1,
            "_id": 1
          },
          "indexName": "category_1_sticky_-1_lastPostAt_-1__id_1",
          "isMultiKey": false,
          "multiKeyPaths": {
            "category": [ ],
            "sticky": [ ],
            "lastPostAt": [ ],
            "_id": [ ]
          },
          "isUnique": false,
          "isSparse": false,
          "isPartial": false,
          "indexVersion": 2,
          "direction": "forward",
          "indexBounds": {
            "category": [
              "[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
            ],
            "sticky": [
              "[MaxKey, MinKey]"
            ],
            "lastPostAt": [
              "[MaxKey, MinKey]"
            ],
            "_id": [
              "(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
            ]
          }
        }
      }
    }
Run Code Online (Sandbox Code Playgroud)

请注意,获胜计划不包含SORT_KEY_GENERATOR阶段。这意味着可以充分利用索引来响应排序查询。