【问题标题】:Covered Queries do not work with partial indices覆盖查询不适用于部分索引
【发布时间】:2020-06-12 10:40:38
【问题描述】:

更新:我创建了一张票:https://jira.mongodb.org/browse/SERVER-48777

请看下面的例子。为什么第二个查询会执行文档查找?

db.createCollection('inventory')
db.getCollection('inventory').insertMany([

    { type: 'food', color: 'yellow', name: 'banana' },
    { type: 'food', color: 'red',    name: 'cherry' },
    { type: 'car',  color: 'yellow', name: 'taxi' }

])

db.getCollection('inventory').createIndex({ type: 1, name: 1 }, { name: 'byType' })
db.getCollection('inventory').createIndex({ name: 1 },          { name: 'onlyRed', partialFilterExpression: {  color: 'red' } })

// find cherry by type -> totalDocsExamined = 0 (as expected)
db.getCollection('inventory').find({ type: 'food', name: /c/ }, { name: 1, _id: 0 }).explain('executionStats').executionStats.totalDocsExamined

// find cherry by color -> totalDocsExamined = 1 :-(
db.getCollection('inventory').find({ color: 'red', name: /c/ }, { name: 1, _id: 0 }).explain('executionStats').executionStats.totalDocsExamined

上面例子的索引是:

db.getCollection('inventory').getIndexes()
[
    {
        "v" : 2,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_"
    },
    {
        "v" : 2,
        "key" : {
            "type" : 1.0,
            "name" : 1.0
        },
        "name" : "byType"
    },
    {
        "v" : 2,
        "key" : {
            "name" : 1.0
        },
        "name" : "onlyRed",
        "partialFilterExpression" : {
            "color" : "red"
        }
    }
]

以下所有版本都显示此行为:4.2.1、4.2.7、4.4.0-rc9

我在 Covered QueriesPartial Indexes 的文档中找不到任何内容,为什么不应该支持它。

查询1的解释:

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "demo.inventory",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "$and" : [ 
                {
                    "type" : {
                        "$eq" : "food"
                    }
                }, 
                {
                    "name" : {
                        "$regex" : "c"
                    }
                }
            ]
        },
        "winningPlan" : {
            "stage" : "PROJECTION_COVERED",
            "transformBy" : {
                "name" : 1.0,
                "_id" : 0.0
            },
            "inputStage" : {
                "stage" : "IXSCAN",
                "filter" : {
                    "name" : {
                        "$regex" : "c"
                    }
                },
                "keyPattern" : {
                    "type" : 1.0,
                    "name" : 1.0
                },
                "indexName" : "byType",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "type" : [],
                    "name" : []
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "type" : [ 
                        "[\"food\", \"food\"]"
                    ],
                    "name" : [ 
                        "[\"\", {})", 
                        "[/c/, /c/]"
                    ]
                }
            }
        },
        "rejectedPlans" : []
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1,
        "executionTimeMillis" : 0,
        "totalKeysExamined" : 2,
        "totalDocsExamined" : 0,
        "executionStages" : {
            "stage" : "PROJECTION_COVERED",
            "nReturned" : 1,
            "executionTimeMillisEstimate" : 0,
            "works" : 3,
            "advanced" : 1,
            "needTime" : 1,
            "needYield" : 0,
            "saveState" : 0,
            "restoreState" : 0,
            "isEOF" : 1,
            "transformBy" : {
                "name" : 1.0,
                "_id" : 0.0
            },
            "inputStage" : {
                "stage" : "IXSCAN",
                "filter" : {
                    "name" : {
                        "$regex" : "c"
                    }
                },
                "nReturned" : 1,
                "executionTimeMillisEstimate" : 0,
                "works" : 3,
                "advanced" : 1,
                "needTime" : 1,
                "needYield" : 0,
                "saveState" : 0,
                "restoreState" : 0,
                "isEOF" : 1,
                "keyPattern" : {
                    "type" : 1.0,
                    "name" : 1.0
                },
                "indexName" : "byType",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "type" : [],
                    "name" : []
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "type" : [ 
                        "[\"food\", \"food\"]"
                    ],
                    "name" : [ 
                        "[\"\", {})", 
                        "[/c/, /c/]"
                    ]
                },
                "keysExamined" : 2,
                "seeks" : 1,
                "dupsTested" : 0,
                "dupsDropped" : 0
            }
        }
    },
    "serverInfo" : {
        "host" : "mongo",
        "port" : 27017,
        "version" : "4.4.0-rc9",
        "gitVersion" : "bea79f76addfe4b754c8696db029c5b3c762041c"
    },
    "ok" : 1.0,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1591984674, 1),
        "signature" : {
            "hash" : { "$binary" : "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "$type" : "00" },
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1591984674, 1)
}

对query2的解释(使用部分索引):

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "demo.inventory",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "$and" : [ 
                {
                    "color" : {
                        "$eq" : "red"
                    }
                }, 
                {
                    "name" : {
                        "$regex" : "c"
                    }
                }
            ]
        },
        "winningPlan" : {
            "stage" : "PROJECTION_SIMPLE",
            "transformBy" : {
                "name" : 1.0,
                "_id" : 0.0
            },
            "inputStage" : {
                "stage" : "FETCH",
                "filter" : {
                    "color" : {
                        "$eq" : "red"
                    }
                },
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "filter" : {
                        "name" : {
                            "$regex" : "c"
                        }
                    },
                    "keyPattern" : {
                        "name" : 1.0
                    },
                    "indexName" : "onlyRed",
                    "isMultiKey" : false,
                    "multiKeyPaths" : {
                        "name" : []
                    },
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : true,
                    "indexVersion" : 2,
                    "direction" : "forward",
                    "indexBounds" : {
                        "name" : [ 
                            "[\"\", {})", 
                            "[/c/, /c/]"
                        ]
                    }
                }
            }
        },
        "rejectedPlans" : []
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1,
        "executionTimeMillis" : 0,
        "totalKeysExamined" : 1,
        "totalDocsExamined" : 1,
        "executionStages" : {
            "stage" : "PROJECTION_SIMPLE",
            "nReturned" : 1,
            "executionTimeMillisEstimate" : 0,
            "works" : 2,
            "advanced" : 1,
            "needTime" : 0,
            "needYield" : 0,
            "saveState" : 0,
            "restoreState" : 0,
            "isEOF" : 1,
            "transformBy" : {
                "name" : 1.0,
                "_id" : 0.0
            },
            "inputStage" : {
                "stage" : "FETCH",
                "filter" : {
                    "color" : {
                        "$eq" : "red"
                    }
                },
                "nReturned" : 1,
                "executionTimeMillisEstimate" : 0,
                "works" : 2,
                "advanced" : 1,
                "needTime" : 0,
                "needYield" : 0,
                "saveState" : 0,
                "restoreState" : 0,
                "isEOF" : 1,
                "docsExamined" : 1,
                "alreadyHasObj" : 0,
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "filter" : {
                        "name" : {
                            "$regex" : "c"
                        }
                    },
                    "nReturned" : 1,
                    "executionTimeMillisEstimate" : 0,
                    "works" : 2,
                    "advanced" : 1,
                    "needTime" : 0,
                    "needYield" : 0,
                    "saveState" : 0,
                    "restoreState" : 0,
                    "isEOF" : 1,
                    "keyPattern" : {
                        "name" : 1.0
                    },
                    "indexName" : "onlyRed",
                    "isMultiKey" : false,
                    "multiKeyPaths" : {
                        "name" : []
                    },
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : true,
                    "indexVersion" : 2,
                    "direction" : "forward",
                    "indexBounds" : {
                        "name" : [ 
                            "[\"\", {})", 
                            "[/c/, /c/]"
                        ]
                    },
                    "keysExamined" : 1,
                    "seeks" : 1,
                    "dupsTested" : 0,
                    "dupsDropped" : 0
                }
            }
        }
    },
    "serverInfo" : {
        "host" : "mongo",
        "port" : 27017,
        "version" : "4.4.0-rc9",
        "gitVersion" : "bea79f76addfe4b754c8696db029c5b3c762041c"
    },
    "ok" : 1.0,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1591984064, 1),
        "signature" : {
            "hash" : { "$binary" : "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "$type" : "00" },
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1591984064, 1)
}

【问题讨论】:

  • 我完全不熟悉 Mongo 中的索引,但我可以告诉你,一般来说,任何数据库都可以选择不使用索引,如果这样做不会提高性能的话。至少对于关系 SQL 来说是这样。
  • 这个索引创建语句正确吗? db.getCollection('inventory').createIndex({ name: 1 }, { name: 'onlyRed' }, { partialFilterExpression: { color: 'red' } })。是否值得检查您的索引以确保您正在创建您想要的索引?
  • @willis 在我的示例中确实有一个错字。对此感到抱歉。我纠正了它。我的真实收藏的索引是不同的。我试图在这里找到一个最小的例子。
  • 两个查询的完整查询计划是什么?
  • 似乎使用了索引,然后服务器执行了一个不需要的过滤阶段。我会将此报告给jira.mongodb.org/browse/SERVER

标签: mongodb mongodb-query mongodb-indexes


【解决方案1】:

虽然您知道在 MongoDB Jira 中跟踪的这些类型的索引存在限制,但有一个简单的解决方法 - 将部分索引表达式中的字段作为键添加到索引定义中。这将允许选择仅索引计划。

【讨论】:

    【解决方案2】:

    原来这是一个已知的错误,已在此处跟踪:https://jira.mongodb.org/browse/SERVER-26580

    问题的创建日期是 2016 年 10 月 11 日。:-(

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-23
      • 2017-11-15
      • 1970-01-01
      • 2013-12-20
      • 1970-01-01
      • 2015-03-18
      • 2014-02-21
      • 2017-01-12
      相关资源
      最近更新 更多