热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

MongoDB中哪几种情况下的索引选择策略

这篇文章主要给大家介绍了关于MongoDB中哪几种情况下的索引选择策略的相关资料,文中通过图文以及实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

一、MongoDB如何选择索引

如果我们在Collection建了5个index,那么当我们查询的时候,MongoDB会根据查询语句的筛选条件、sort排序等来定位可以使用的index作为候选索引;然后MongoDB会创建对应数量的查询计划,并分别使用不同线程执行查询计划,最终会选择一个执行最快的index;但是这个选择也不是一成不变的,后续还会有一段时间根据实际执行情况动态调整;

二、数据准备

for(let i = 0;i<1000000;i++){
    db.users.insertOne({
        "id":i,
        "name":'user'+i,
        "age":Math.floor(Math.random()*120),
        "created":new Date(ISODate().getTime() - 1000 * 60*i)
    });
}

三、正则对index的使用

MongoDB支持正则查询,在特定的情况其也是可以利用index获得查询性能的提升;

虽然MongDB执行正则会最大限度的使用index,但是不同的用法还是会影响对index的利用程度的;

执行以下普通正则表达式

从queryPlanner.winningPlan部分的COLLSCAN,可以看到正则表达式默认会进行全表的扫描;

从executionStats.executionStages部分可以看到COLLSCAN共扫描了1000000个文档,并返回1111个文档,总耗时794ms;

db.users.find({
    name:/user999/
    }).explain('executionStats')
    
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,        
        "winningPlan" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "name" : {
                    "$regex" : "user999"
                }
            },
            "direction" : "forward"
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1111,
        "executionTimeMillis" : 909,
        "totalKeysExamined" : 0,
        "totalDocsExamined" : 1000000,
        "executionStages" : {
            "stage" : "COLLSCAN",
            "filter" : {
                "name" : {
                    "$regex" : "user999"
                }
            },
            "nReturned" : 1111,
            "executionTimeMillisEstimate" : 794,
            "works" : 1000002,
            "advanced" : 1111,
            "needTime" : 998890,
            "needYield" : 0,
            "saveState" : 7830,
            "restoreState" : 7830,
            "isEOF" : 1,
            "invalidates" : 0,
            "direction" : "forward",
            "docsExamined" : 1000000
        }
    }
}

创建一个包含name的index;

db.users.createIndex({name:1})

再次执行上边的查询,可以看到使用了我们新建的name_1索引;但是从执行状态来看,还是扫描了全体的索引的key,并不能很好的利用index;

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "name" : {
                "$regex" : "user999"
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "filter" : {
                    "name" : {
                        "$regex" : "user999"
                    }
                },
                "keyPattern" : {
                    "name" : 1
                },
                "indexName" : "name_1"                
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1111,
        "executionTimeMillis" : 971,
        "totalKeysExamined" : 1000000,
        "totalDocsExamined" : 1111,
        "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 1111,
            "executionTimeMillisEstimate" : 887,            
            "docsExamined" : 1111,
            "alreadyHasObj" : 0,
            "inputStage" : {
                "stage" : "IXSCAN",
                "filter" : {
                    "name" : {
                        "$regex" : "user999"
                    }
                },
                "nReturned" : 1111,
                "executionTimeMillisEstimate" : 876,              
                "keyPattern" : {
                    "name" : 1
                },
                "indexName" : "name_1",              
                "keysExamined" : 1000000
            }
        }
    }
}

使用前缀匹配的话可以最大限度的利用index,从执行状态可以看到只检测了1111个index key;

db.users.find({
    name:/^user999/
    }).explain('executionStats')
    
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "name" : {
                "$regex" : "^user999"
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "name" : 1
                },
                "indexName" : "name_1"                
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1111,
        "executionTimeMillis" : 2,
        "totalKeysExamined" : 1111,
        "totalDocsExamined" : 1111,
        "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 1111,
            "executionTimeMillisEstimate" : 0
            "docsExamined" : 1111            
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 1111,
                "executionTimeMillisEstimate" : 0,
                "indexName" : "name_1",
                "keysExamined" : 1111
            }
        }
    }
}
    

即使是前缀匹配,如果忽略大小写的话也无法充分利用index了;

db.users.find({
    name:/^user999/i
    }).explain('executionStats')
    
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "name" : {
                "$regex" : "user999",
                "$options" : "i"
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "filter" : {
                    "name" : {
                        "$regex" : "user999",
                        "$options" : "i"
                    }
                },
                "keyPattern" : {
                    "name" : 1
                },
                "indexName" : "name_1"
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1111,
        "executionTimeMillis" : 943,
        "totalKeysExamined" : 1000000,
        "totalDocsExamined" : 1111,
        "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 1111,
            "executionTimeMillisEstimate" : 833,
            "works" : 1000001,
            "inputStage" : {
                "stage" : "IXSCAN",
                "filter" : {
                    "name" : {
                        "$regex" : "user999",
                        "$options" : "i"
                    }
                },
                "nReturned" : 1111,
                "executionTimeMillisEstimate" : 833,
                "keyPattern" : {
                    "name" : 1
                },
                "indexName" : "name_1"
                "keysExamined" : 1000000
            }
        }
    }
}

四、$or从句对索引的利用

MongoDB执行$or从句的时候,会将所有的从句作为逻辑的整体,要不就都使用index,要不就都进行全表扫描;

执行以下的查询语句;

db.users.find({
    $or:[
        {name:/^user666/},
        {age:{$gte:80}}
    ]
    }).explain('executionStats')

在只有name_1这个index的时候,我们可以看到MongoDB进行了全表扫描,全表扫描的时候进行$or从句的过滤;

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "$or" : [
                {
                    "age" : {
                        "$gte" : 20
                    }
                },
                {
                    "name" : {
                        "$regex" : "^user666"
                    }
                }
            ]
        },
        "winningPlan" : {
            "stage" : "SUBPLAN",
            "inputStage" : {
                "stage" : "COLLSCAN",
                "filter" : {
                    "$or" : [
                        {
                            "age" : {
                                "$gte" : 20
                            }
                        },
                        {
                            "name" : {
                                "$regex" : "^user666"
                            }
                        }
                    ]
                },
                "direction" : "forward"
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 833995,
        "executionTimeMillis" : 576,
        "totalKeysExamined" : 0,
        "totalDocsExamined" : 1000000,
        "executionStages" : {
            "stage" : "SUBPLAN",
            "nReturned" : 833995,
            "executionTimeMillisEstimate" : 447,           
            "inputStage" : {
                "stage" : "COLLSCAN",
                "filter" : {
                    "$or" : [
                        {
                            "age" : {
                                "$gte" : 20
                            }
                        },
                        {
                            "name" : {
                                "$regex" : "^user666"
                            }
                        }
                    ]
                },
                "nReturned" : 833995,
                "executionTimeMillisEstimate" : 447,               
                "docsExamined" : 1000000
            }
        }
    }
}

我们对name字段新建一个index;

db.users.createIndex({age:1})

再次执行以上的查询语句,这次可以看到每个从句都利用了index,并且每个从句会单独执行并最终进行or操作;

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "$or" : [
                {
                    "age" : {
                        "$gte" : 80
                    }
                },
                {
                    "name" : {
                        "$regex" : "^user666"
                    }
                }
            ]
        },
        "winningPlan" : {
            "stage" : "SUBPLAN",
            "inputStage" : {
                "stage" : "FETCH",
                "inputStage" : {
                    "stage" : "OR",
                    "inputStages" : [
                        {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "name" : 1
                            },
                            "indexName" : "name_1",
                            "isMultiKey" : false,
                            "multiKeyPaths" : {
                                "name" : [ ]
                            },
                            "isUnique" : false,
                            "isSparse" : false,
                            "isPartial" : false,
                            "indexVersion" : 2,
                            "direction" : "forward",
                            "indexBounds" : {
                                "name" : [
                                    "[\"user666\", \"user667\")",
                                    "[/^user666/, /^user666/]"
                                ]
                            }
                        },
                        {
                            "stage" : "IXSCAN",
                            "keyPattern" : {
                                "age" : 1
                            },
                            "indexName" : "age_1",
                            "isMultiKey" : false,
                            "multiKeyPaths" : {
                                "age" : [ ]
                            },
                            "isUnique" : false,
                            "isSparse" : false,
                            "isPartial" : false,
                            "indexVersion" : 2,
                            "direction" : "forward",
                            "indexBounds" : {
                                "age" : [
                                    "[80.0, inf.0]"
                                ]
                            }
                        }
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 333736,
        "executionTimeMillis" : 741,
        "totalKeysExamined" : 334102,
        "totalDocsExamined" : 333736,
        "executionStages" : {
            "stage" : "SUBPLAN",
            "nReturned" : 333736,
            "executionTimeMillisEstimate" : 703,
            "inputStage" : {
                "stage" : "FETCH",
                "nReturned" : 333736,
                "executionTimeMillisEstimate" : 682
                "docsExamined" : 333736,                
                "inputStage" : {
                    "stage" : "OR",
                    "nReturned" : 333736,
                    "executionTimeMillisEstimate" : 366,
                    "inputStages" : [
                        {
                            "stage" : "IXSCAN",
                            "nReturned" : 1111,
                            "executionTimeMillisEstimate" : 0,
                            "keyPattern" : {
                                "name" : 1
                            },
                            "indexName" : "name_1",
                            "indexBounds" : {
                                "name" : [
                                    "[\"user666\", \"user667\")",
                                    "[/^user666/, /^user666/]"
                                ]
                            },
                            "keysExamined" : 1112
                        },
                        {
                            "stage" : "IXSCAN",
                            "nReturned" : 332990,
                            "executionTimeMillisEstimate" : 212,                          
                            "keyPattern" : {
                                "age" : 1
                            },
                            "indexName" : "age_1",                           
                            "indexBounds" : {
                                "age" : [
                                    "[80.0, inf.0]"
                                ]
                            },
                            "keysExamined" : 332990
                        }
                    ]
                }
            }
        }
    }
}

五、sort对索引的利用

如果sort操作无法利用index,则MongoDB就会在内存中排序数据,并且数据量一大就会报错;

db.users.find().sort({created: -1}).explain('executionStats')

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            
        },
        "winningPlan" : {
            "stage" : "SORT",
            "sortPattern" : {
                "created" : -1
            },
            "inputStage" : {
                "stage" : "SORT_KEY_GENERATOR",
                "inputStage" : {
                    "stage" : "COLLSCAN",
                    "direction" : "forward"
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : false,
        "errorMessage" : "Exec error resulting in state FAILURE :: caused by :: Sort operation used more than the maximum 33554432 bytes of RAM. Add an index, or specify a smaller limit.",
        "errorCode" : 96,
        "nReturned" : 0,
        "executionTimeMillis" : 959,
        "totalKeysExamined" : 0,
        "totalDocsExamined" : 361996,
        "executionStages" : {
            "stage" : "SORT",
            "nReturned" : 0,
            "executionTimeMillisEstimate" : 922,
            "sortPattern" : {
                "created" : -1
            },
            "memUsage" : 33554518,
            "memLimit" : 33554432,
            "inputStage" : {
                "stage" : "SORT_KEY_GENERATOR",
                "nReturned" : 361996,
                "executionTimeMillisEstimate" : 590,
                "inputStage" : {
                    "stage" : "COLLSCAN",
                    "nReturned" : 361996,
                    "executionTimeMillisEstimate" : 147,
                    "direction" : "forward",
                    "docsExamined" : 361996
                }
            }
        }
    }
}

如果是单字段index,sort从两个方向都可以充分利用index;可以看到MongoDB直接按照index的顺序返回结果,直接就没有sort阶段了;

db.users.find().sort({name: -1}).explain('executionStats')
      
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "name" : 1
                },
                "indexName" : "name_1",
                "direction" : "backward",
                "indexBounds" : {
                    "name" : [
                        "[MaxKey, MinKey]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1000000,
        "executionTimeMillis" : 1317,
        "totalKeysExamined" : 1000000,
        "totalDocsExamined" : 1000000,
        "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 1000000,
            "executionTimeMillisEstimate" : 1180,
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 1000000,
                "executionTimeMillisEstimate" : 560,
                "keyPattern" : {
                    "name" : 1
                },
                "indexName" : "name_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "name" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "backward",
                "indexBounds" : {
                    "name" : [
                        "[MaxKey, MinKey]"
                    ]
                },
                "keysExamined" : 1000000,
                "seeks" : 1,
                "dupsTested" : 0,
                "dupsDropped" : 0,
                "seenInvalidated" : 0
            }
        }
    }
}
  

对于复合索引,sort除了可以从整体上从两个方向利用index,也可以利用index的前缀索引和非前缀局部索引;

新建复合索引

db.users.createIndex({created:-1, name:1, age:1})

按照复合索引的反方向进行整体排序;

db.users.find().sort({created:1, name:-1, age:-1}).explain('executionStats')

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "created" : -1,
                    "name" : 1,
                    "age" : 1
                },
                "indexName" : "created_-1_name_1_age_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "created" : [ ],
                    "name" : [ ],
                    "age" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "backward",
                "indexBounds" : {
                    "created" : [
                        "[MinKey, MaxKey]"
                    ],
                    "name" : [
                        "[MaxKey, MinKey]"
                    ],
                    "age" : [
                        "[MaxKey, MinKey]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1000000,
        "executionTimeMillis" : 1518,
        "totalKeysExamined" : 1000000,
        "totalDocsExamined" : 1000000,
        "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 1000000,
            "executionTimeMillisEstimate" : 1364,
            "docsExamined" : 1000000,
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 1000000,
                "executionTimeMillisEstimate" : 816,
                "keyPattern" : {
                    "created" : -1,
                    "name" : 1,
                    "age" : 1
                },
                "indexName" : "created_-1_name_1_age_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "created" : [ ],
                    "name" : [ ],
                    "age" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "backward",
                "indexBounds" : {
                    "created" : [
                        "[MinKey, MaxKey]"
                    ],
                    "name" : [
                        "[MaxKey, MinKey]"
                    ],
                    "age" : [
                        "[MaxKey, MinKey]"
                    ]
                },
                "keysExamined" : 1000000
            }
        }
    }
}

排序使用索引前缀,也需要保证字段的顺序,但是可以反方向排序;

db.users.find().sort({created:1, name:-1, age:-1}).explain('executionStats')

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "created" : -1,
                    "name" : 1,
                    "age" : 1
                },
                "indexName" : "created_-1_name_1_age_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "created" : [ ],
                    "name" : [ ],
                    "age" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "backward",
                "indexBounds" : {
                    "created" : [
                        "[MinKey, MaxKey]"
                    ],
                    "name" : [
                        "[MaxKey, MinKey]"
                    ],
                    "age" : [
                        "[MaxKey, MinKey]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 1000000,
        "executionTimeMillis" : 1487,
        "totalKeysExamined" : 1000000,
        "totalDocsExamined" : 1000000,
        "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 1000000,
            "executionTimeMillisEstimate" : 1339,
            "works" : 1000001,
            "advanced" : 1000000,
            "needTime" : 0,
            "needYield" : 0,
            "saveState" : 7845,
            "restoreState" : 7845,
            "isEOF" : 1,
            "invalidates" : 0,
            "docsExamined" : 1000000,
            "alreadyHasObj" : 0,
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 1000000,
                "executionTimeMillisEstimate" : 769,
                "works" : 1000001,
                "advanced" : 1000000,
                "needTime" : 0,
                "needYield" : 0,
                "saveState" : 7845,
                "restoreState" : 7845,
                "isEOF" : 1,
                "invalidates" : 0,
                "keyPattern" : {
                    "created" : -1,
                    "name" : 1,
                    "age" : 1
                },
                "indexName" : "created_-1_name_1_age_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "created" : [ ],
                    "name" : [ ],
                    "age" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "backward",
                "indexBounds" : {
                    "created" : [
                        "[MinKey, MaxKey]"
                    ],
                    "name" : [
                        "[MaxKey, MinKey]"
                    ],
                    "age" : [
                        "[MaxKey, MinKey]"
                    ]
                },
                "keysExamined" : 1000000,
                "seeks" : 1,
                "dupsTested" : 0,
                "dupsDropped" : 0,
                "seenInvalidated" : 0
            }
        }
    }
}

排序如果使用的是非前缀的局部字典排序,name需要保证前边的字段是等值筛选操作才行;

db.users.find({created:new Date("2021-10-30T08:17:01.184Z")}).sort({name:-1}).explain('executionStats')

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "created" : {
                "$eq" : ISODate("2021-10-30T08:17:01.184Z")
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "created" : -1,
                    "name" : 1,
                    "age" : 1
                },
                "indexName" : "created_-1_name_1_age_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "created" : [ ],
                    "name" : [ ],
                    "age" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "backward",
                "indexBounds" : {
                    "created" : [
                        "[new Date(1635581821184), new Date(1635581821184)]"
                    ],
                    "name" : [
                        "[MaxKey, MinKey]"
                    ],
                    "age" : [
                        "[MaxKey, MinKey]"
                    ]
                }
            }
        },
        "rejectedPlans" : [ ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 0,
        "executionTimeMillis" : 0,
        "totalKeysExamined" : 0,
        "totalDocsExamined" : 0,
        "executionStages" : {
            "stage" : "FETCH",
            "nReturned" : 0,
            "executionTimeMillisEstimate" : 0,
            "works" : 1,
            "advanced" : 0,
            "needTime" : 0,
            "needYield" : 0,
            "saveState" : 0,
            "restoreState" : 0,
            "isEOF" : 1,
            "invalidates" : 0,
            "docsExamined" : 0,
            "alreadyHasObj" : 0,
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 0,
                "executionTimeMillisEstimate" : 0,
                "works" : 1,
                "advanced" : 0,
                "needTime" : 0,
                "needYield" : 0,
                "saveState" : 0,
                "restoreState" : 0,
                "isEOF" : 1,
                "invalidates" : 0,
                "keyPattern" : {
                    "created" : -1,
                    "name" : 1,
                    "age" : 1
                },
                "indexName" : "created_-1_name_1_age_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "created" : [ ],
                    "name" : [ ],
                    "age" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "backward",
                "indexBounds" : {
                    "created" : [
                        "[new Date(1635581821184), new Date(1635581821184)]"
                    ],
                    "name" : [
                        "[MaxKey, MinKey]"
                    ],
                    "age" : [
                        "[MaxKey, MinKey]"
                    ]
                },
                "keysExamined" : 0,
                "seeks" : 1,
                "dupsTested" : 0,
                "dupsDropped" : 0,
                "seenInvalidated" : 0
            }
        }
    }
}

六、搜索数据对索引命中的影响

MongoDB对index的选择是受到实际场景的数据影响比较大的,即与实际数据的分布规律有关,也跟实际筛选出来的数据有关系;所以我们对索引的优化和测试都需要考虑实际的数据场景才行;

由于name的字段值筛选出来的key太多,不能充分利用index,所以MongoDB拒绝了name_1并选择了age_1;

db.users.find({
        name:/^user/,
        age:{$gte:110}
    }).explain('executionStats')
    
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "$and" : [
                {
                    "age" : {
                        "$gte" : 110
                    }
                },
                {
                    "name" : {
                        "$regex" : "^user"
                    }
                }
            ]
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "filter" : {
                "name" : {
                    "$regex" : "^user"
                }
            },
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "age" : 1
                },
                "indexName" : "age_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "age" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "age" : [
                        "[110.0, inf.0]"
                    ]
                }
            }
        },
        "rejectedPlans" : [
            {
                "stage" : "FETCH",
                "filter" : {
                    "age" : {
                        "$gte" : 110
                    }
                },
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "name" : 1
                    },
                    "indexName" : "name_1",
                    "isMultiKey" : false,
                    "multiKeyPaths" : {
                        "name" : [ ]
                    },
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 2,
                    "direction" : "forward",
                    "indexBounds" : {
                        "name" : [
                            "[\"user\", \"uses\")",
                            "[/^user/, /^user/]"
                        ]
                    }
                }
            }
        ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 83215,
        "executionTimeMillis" : 246,
        "totalKeysExamined" : 83215,
        "totalDocsExamined" : 83215,
        "executionStages" : {
            "stage" : "FETCH",
            "filter" : {
                "name" : {
                    "$regex" : "^user"
                }
            },
            "nReturned" : 83215,
            "executionTimeMillisEstimate" : 232,
            "works" : 83216,
            "advanced" : 83215,
            "needTime" : 0,
            "needYield" : 0,
            "saveState" : 658,
            "restoreState" : 658,
            "isEOF" : 1,
            "invalidates" : 0,
            "docsExamined" : 83215,
            "alreadyHasObj" : 0,
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 83215,
                "executionTimeMillisEstimate" : 43,
                "works" : 83216,
                "advanced" : 83215,
                "needTime" : 0,
                "needYield" : 0,
                "saveState" : 658,
                "restoreState" : 658,
                "isEOF" : 1,
                "invalidates" : 0,
                "keyPattern" : {
                    "age" : 1
                },
                "indexName" : "age_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "age" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "age" : [
                        "[110.0, inf.0]"
                    ]
                },
                "keysExamined" : 83215,
                "seeks" : 1,
                "dupsTested" : 0,
                "dupsDropped" : 0,
                "seenInvalidated" : 0
            }
        }
    }
}
    

我们修改一下name筛选条件的值,进一步缩小命中的范围,可以看到这次MongoDB选择了name_1;

db.users.find({
        name:/^user8888/,
        age:{$gte:110}
    }).explain('executionStats')
    

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "$and" : [
                {
                    "age" : {
                        "$gte" : 110
                    }
                },
                {
                    "name" : {
                        "$regex" : "^user8888"
                    }
                }
            ]
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "filter" : {
                "age" : {
                    "$gte" : 110
                }
            },
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "name" : 1
                },
                "indexName" : "name_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "name" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "name" : [
                        "[\"user8888\", \"user8889\")",
                        "[/^user8888/, /^user8888/]"
                    ]
                }
            }
        },
        "rejectedPlans" : [
            {
                "stage" : "FETCH",
                "filter" : {
                    "name" : {
                        "$regex" : "^user8888"
                    }
                },
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "age" : 1
                    },
                    "indexName" : "age_1",
                    "isMultiKey" : false,
                    "multiKeyPaths" : {
                        "age" : [ ]
                    },
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 2,
                    "direction" : "forward",
                    "indexBounds" : {
                        "age" : [
                            "[110.0, inf.0]"
                        ]
                    }
                }
            }
        ]
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 10,
        "executionTimeMillis" : 0,
        "totalKeysExamined" : 112,
        "totalDocsExamined" : 111,
        "executionStages" : {
            "stage" : "FETCH",
            "filter" : {
                "age" : {
                    "$gte" : 110
                }
            },
            "nReturned" : 10,
            "executionTimeMillisEstimate" : 0,
            "works" : 114,
            "advanced" : 10,
            "needTime" : 102,
            "needYield" : 0,
            "saveState" : 1,
            "restoreState" : 1,
            "isEOF" : 1,
            "invalidates" : 0,
            "docsExamined" : 111,
            "alreadyHasObj" : 0,
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 111,
                "executionTimeMillisEstimate" : 0,
                "works" : 113,
                "advanced" : 111,
                "needTime" : 1,
                "needYield" : 0,
                "saveState" : 1,
                "restoreState" : 1,
                "isEOF" : 1,
                "invalidates" : 0,
                "keyPattern" : {
                    "name" : 1
                },
                "indexName" : "name_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "name" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "name" : [
                        "[\"user8888\", \"user8889\")",
                        "[/^user8888/, /^user8888/]"
                    ]
                },
                "keysExamined" : 112,
                "seeks" : 2,
                "dupsTested" : 0,
                "dupsDropped" : 0,
                "seenInvalidated" : 0
            }
        }
    }
}

总结

到此这篇关于MongoDB中哪几种情况下的索引选择策略的文章就介绍到这了,更多相关MongoDB索引选择策略内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 本文整理了315道Python基础题目及答案,帮助读者检验学习成果。文章介绍了学习Python的途径、Python与其他编程语言的对比、解释型和编译型编程语言的简述、Python解释器的种类和特点、位和字节的关系、以及至少5个PEP8规范。对于想要检验自己学习成果的读者,这些题目将是一个不错的选择。请注意,答案在视频中,本文不提供答案。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 花瓣|目标值_Compose 动画边学边做夏日彩虹
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Compose动画边学边做-夏日彩虹相关的知识,希望对你有一定的参考价值。引言Comp ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 本文介绍了游标的使用方法,并以一个水果供应商数据库为例进行了说明。首先创建了一个名为fruits的表,包含了水果的id、供应商id、名称和价格等字段。然后使用游标查询了水果的名称和价格,并将结果输出。最后对游标进行了关闭操作。通过本文可以了解到游标在数据库操作中的应用。 ... [详细]
  • 统一知识图谱学习和建议:更好地理解用户偏好
    本文介绍了一种将知识图谱纳入推荐系统的方法,以提高推荐的准确性和可解释性。与现有方法不同的是,本方法考虑了知识图谱的不完整性,并在知识图谱中传输关系信息,以更好地理解用户的偏好。通过大量实验,验证了本方法在推荐任务和知识图谱完成任务上的优势。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • Explain如何助力SQL语句的优化及其分析方法
    本文介绍了Explain如何助力SQL语句的优化以及分析方法。Explain是一个数据库SQL语句的模拟器,通过对SQL语句的模拟返回一个性能分析表,从而帮助工程师了解程序运行缓慢的原因。文章还介绍了Explain运行方法以及如何分析Explain表格中各个字段的含义。MySQL 5.5开始支持Explain功能,但仅限于select语句,而MySQL 5.7逐渐支持对update、delete和insert语句的模拟和分析。 ... [详细]
  • Python使用Pillow包生成验证码图片的方法
    本文介绍了使用Python中的Pillow包生成验证码图片的方法。通过随机生成数字和符号,并添加干扰象素,生成一幅验证码图片。需要配置好Python环境,并安装Pillow库。代码实现包括导入Pillow包和随机模块,定义随机生成字母、数字和字体颜色的函数。 ... [详细]
  • 如何使用Python从工程图图像中提取底部的方法?
    本文介绍了使用Python从工程图图像中提取底部的方法。首先将输入图片转换为灰度图像,并进行高斯模糊和阈值处理。然后通过填充潜在的轮廓以及使用轮廓逼近和矩形核进行过滤,去除非矩形轮廓。最后通过查找轮廓并使用轮廓近似、宽高比和轮廓区域进行过滤,隔离所需的底部轮廓,并使用Numpy切片提取底部模板部分。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • 本文介绍了DataTables插件的官方网站以及其基本特点和使用方法,包括分页处理、数据过滤、数据排序、数据类型检测、列宽度自动适应、CSS定制样式、隐藏列等功能。同时还介绍了其易用性、可扩展性和灵活性,以及国际化和动态创建表格的功能。此外,还提供了参数初始化和延迟加载的示例代码。 ... [详细]
  • #define_CRT_SECURE_NO_WARNINGS#includelist.h#includevoidSListInit(PNode*pHead ... [详细]
  • 抽空写了一个ICON图标的转换程序
    抽空写了一个ICON图标的转换程序,支持png\jpe\bmp格式到ico的转换。具体的程序就在下面,如果看的人多,过两天再把思路写一下。 ... [详细]
author-avatar
眾于-晴
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有