首页 > 数据库 > 国内数据库 > 云上MongoDB常见索引问题及最优索引规则大全
1 ►
背景
2 ►
MongoDB执行计划
cmgo-xxxx:PRIMARY> db.test4.find({xxxx}).explain("queryPlanner") { "queryPlanner" : { "parsedQuery" : { ......;//查询条件对应的expression Tree }, "winningPlan" : { //查询优化器选择的最优索引及其该索引对应的执行阶段信息 ......; }, "rejectedPlans" : [ //查询优化器拒绝掉的非最优索引及其该索引对应的执行阶段信息 ......; ] }, ...... }parsedQuery信息
winningPlan信息
"winningPlan" : { "stage" : <STAGE1>, ... "inputStage" : { "stage" : <STAGE2>, ... "inputStage" : { "stage" : <STAGE3>, ... } } }|
|
|
|
|
|
rejectedPlans信息
db.test4.find({xxxx}).explain("executionStats") "executionStats" : { "executionSuccess" : <boolean>, "nReturned" : <int>, "executionTimeMillis" : <int>, "totalKeysExamined" : <int>, "totalDocsExamined" : <int>, "executionStages" : { "stage" : <STAGE1> "nReturned" : <int>, "executionTimeMillisEstimate" : <int>, "works" : <int>, "advanced" : <int>, "needTime" : <int>, "needYield" : <int>, "saveState" : <int>, "restoreState" : <int>, "isEOF" : <boolean>, ... "inputStage" : { "stage" : <STAGE2>, "nReturned" : <int>, "executionTimeMillisEstimate" : <int>, ... "inputStage" : { ... } } }, ... }|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
executionStats输出字段较多,其他字段将在后续《MongoDB内核index索引模块实现原理》中进行进一步说明。
executionStats.totalKeysExamine远大于executionStats .nReturned
executionStats. totalDocsExamined远大于executionStats .nReturned
queryPlanner
3 ►
云上用户建索引 常见问题及优化方法
db.test4.find({"a":"xxx", "b":"xxx", "c":"xxx"}) db.test4.find({"b":"xxx", "a":"xxx", "c":"xxx"}) db.test4.find({"c":"xxx", "a":"xxx", "b":"xxx"}) MongoDB_4.4_shard2:PRIMARY> MongoDB_4.4_shard2:PRIMARY> db.test.find({"a" : 1, "b" : 1, "c" : 1}).explain("executionStats").queryPlanner.winningPlan { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", ...... "indexName" : "a_1_b_1_c_1", ...... } } MongoDB_4.4_shard2:PRIMARY> MongoDB_4.4_shard2:PRIMARY> db.test.find({"b" : 1, "a" : 1, "c" : 1}).explain("executionStats").queryPlanner.winningPlan { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", ...... "indexName" : "a_1_b_1_c_1", ...... } } MongoDB_4.4_shard2:PRIMARY> MongoDB_4.4_shard2:PRIMARY> db.test.find({"c" : 1, "a" : 1, "b" : 1}).explain("executionStats").queryPlanner.winningPlan { "stage" : "FETCH", "inputStage" : { "stage" : "IXSCAN", ...... "indexName" : "a_1_b_1_c_1", ...... } } MongoDB_4.4_shard2:PRIMARY> MongoDB_4.4_shard2:PRIMARY>MongoDB_4.4_shard2:PRIMARY> db.test.find({"a" : 1, "b" : 1, "c" : 1}).hint({"a" : 1, b:1, c:1}).explain("executionStats").executionStats{"nReturned" : 1,"executionTimeMillis" : 0,"totalKeysExamined" : 1,"totalDocsExamined" : 1,......"executionStages" : {"stage" : "FETCH","nReturned" : 1,......"inputStage" : {"stage" : "IXSCAN",......"indexName" : "a_1_c_1_b_1",}}}MongoDB_4.4_shard2:PRIMARY>MongoDB_4.4_shard2:PRIMARY> db.test.find({"a" : 1, "b" : 1, "c" : 1}).hint({"a" : 1, c:1, b:1}).explain("executionStats").executionStats{"nReturned" : 1,"executionTimeMillis" : 0,"totalKeysExamined" : 1,"totalDocsExamined" : 1,"executionStages" : {"stage" : "FETCH","nReturned" : 1,......"inputStage" : {"stage" : "IXSCAN",......"indexName" : "a_1_c_1_b_1",}}}MongoDB_4.4_shard2:PRIMARY>MongoDB_4.4_shard2:PRIMARY> db.test.find({"c" : 1, "a" : 1, "b" : 1}).hint({"a" : 1, c:1, b:1}).explain("executionStats").executionStats{"nReturned" : 1,"executionTimeMillis" : 0,"totalKeysExamined" : 1,"totalDocsExamined" : 1,"executionStages" : {"stage" : "FETCH","nReturned" : 1,......"inputStage" : {"stage" : "IXSCAN",......"indexName" : "a_1_c_1_b_1",}}}
db.test.find({"b" : 2, "c" : 1}) //查询1 db.test.find({"a" : 10, "b" : 5, "c" : 1}) //查询2MongoDB_4.4_shard2:PRIMARY> db.test.find({"b" : 2, "c" : 1}).explain("executionStats"){......"winningPlan" : {"stage" : "FETCH","inputStage" : {"stage" : "IXSCAN",......"indexName" : "c_1_b_1_a_1",......}}}MongoDB_4.4_shard2:PRIMARY>MongoDB_4.4_shard2:PRIMARY> db.test.find({"a" : 10, "b" : 5, "c" : 1}).explain("executionStats"){......"winningPlan" : {"stage" : "FETCH","inputStage" : {"stage" : "IXSCAN",......"indexName" : "c_1_b_1_a_1",......}}}
db.test.find({a:1,b:1}) db.test.find({a:1,c:1})
//两字段非等值查询 db.test.find({a:{$gte:1}, c:{$lte:1}}) MongoDB_4.4_shard1:PRIMARY> MongoDB_4.4_shard1:PRIMARY> db.test.find({a:{$gte:1}, c:{$lte:1}}).explain("executionStats") { "executionStats" : { "nReturned" : 4, "executionTimeMillis" : 0, "totalKeysExamined" : 10, "totalDocsExamined" : 4, "inputStage" : { ...... "indexName" : "a_1_c_1", } } //两字段非等值查询 db.test.find({"d":{$gte:4}, "e":1})MongoDB_4.4_shard1:PRIMARY>MongoDB_4.4_shard1:PRIMARY> db.test.find({ "d":{ $gte:4}, "e":1}).hint({d:1, e:1}).explain( "executionStats"){"executionStats" : {……"totalKeysExamined" : 5,"totalDocsExamined" : 3,......"inputStage" : {"stage" : "IXSCAN","indexName" : "d_1_e_1",......}}MongoDB_4.4_shard1:PRIMARY> db.test.find({ "d":{ $gte:4}, "e":1}).hint({e:1, d:1}).explain( "executionStats"){"executionStats" : {......"totalKeysExamined" : 3,"totalDocsExamined" : 3,......"inputStage" : {"indexName" : "e_1_d_1",......}
//等值+多个不同优先级非等值查询 db.test.find({"a":1, "b":1, "c":{$ne:5}, "e":{$type:"string"}, "f":{$gt:5},"g":{$in:[3,4]}) 查询1 //or中包含两个查询 db.test.find( { $or: [{ b: 0,d:0 }, {"c":1, "a":{$gte:4}} ] } )MongoDB_4 .4_shard1:PRIMARY> db.test.find( { $or: [{ b: 0,d: 0 }, { "c": 1, "a":{$gte: 4}}]}).hint({b: 1, d: 1, c: 1, a: 1}).explain( "executionStats"){"executionStats" : {......"totalKeysExamined" : 10,"totalDocsExamined" : 10,"inputStage" : {......"indexName" : "b_1_d_1_c_1_a_1",}}//创建{b:1,d:1}和{c:1, a:1}两个索引后,优化器选择这两个索引做为最优索引MongoDB_4 .4_shard1:PRIMARY>MongoDB_4 .4_shard1:PRIMARY> db.test.find( { $or: [{ b: 0,d: 0 }, { "c": 1, "a":{$gte: 4}}]}).explain( "executionStats"){"executionStats" : {......"totalKeysExamined" : 2,"totalDocsExamined" : 2,"executionStages" : {"stage" : "SUBPLAN",......"inputStage" : {"stage" : "OR","inputStages" : [{"stage" : "IXSCAN","indexName" : "b_1_d_1",......},{"stage" : "IXSCAN","indexName" : "c_1_a_1",......}]} }}},
//等值查询+or类查询+sort排序查询 db.test.find( {"f":3, g:2, $or: [{ b: 0, d:0 }, {"c":1, "a":6} ] } ) 查询1 ------db.test.find( {"f":3, g:2, b: 0, d:0 } ) //查询2 or--| ------db.test.find( {"f":3, g:2, "c":1, "a":6} ) //查询3MongoDB_4.4_shard1:PRIMARY> db.test.find( {"f":3, g:2, $or: [{ b: 0, d:0 }, {"c":1, "a":6} ] } ).explain("executionStats") { "executionStats" : { ...... "totalKeysExamined" : 7, "totalDocsExamined" : 7, "executionStages" : { "stage" : "FETCH", ...... "inputStage" : { "stage" : "OR", ...... "inputStages" : [ { "stage" : "IXSCAN", "indexName" : "f_1_g_1_c_1_a_1", ...... }, { "stage" : "IXSCAN", "indexName" : "f_1_g_1_b_1_d_1", } ] } } }, }说明:这个例子中可能在一些特殊数据分布场景,最优索引也可能是{f:1, g:1}或者{f:1, g:1, b:1, d:-1}或者{ f:1, g:1, c:1, a:1},这里我们只考虑大部分通用场景。
db.test.find({}).sort({a:1}).limit(2) db.test.find({}).sort({a:-1}).limit(2)MongoDB_4.4_shard1:PRIMARY>MongoDB_4.4_shard1:PRIMARY> db.test.find({}).sort({a:1}).limit(2).explain("executionStats"){......"winningPlan" : {"stage" : "LIMIT","limitAmount" : 2,"inputStage" : {......"indexName" : "a_1",}}},}MongoDB_4.4_shard1:PRIMARY>MongoDB_4.4_shard1:PRIMARY> db.test.find({}).sort({a:-1}).limit(2).explain("executionStats"){......"winningPlan" : {"stage" : "LIMIT","limitAmount" : 2,"inputStage" : {......"indexName" : "a_1",}}},},
//两字段排序查询 db.test.find().sort({a:1, b:-1}).limit(5)//{a:1, b:1}只会有一个字段走索引,另一个字段内存排序MongoDB_4.4_shard1:PRIMARY>MongoDB_4.4_shard1:PRIMARY> db.test.find().sort({a:1, b:-1}).hint({a:1, b:1}).explain("executionStats"){"executionStats" : {"totalKeysExamined" : 15,"totalDocsExamined" : 15,......"inputStage" : {"stage" : "FETCH",......"inputStage" : {"stage" : "SORT",......"inputStage" : {"stage" : "IXSCAN",......"indexName" : "a_1_b_1",}}}}},//{a:1, b:-1}两个字段走索引,不存在内存排序MongoDB_4.4_shard1:PRIMARY>MongoDB_4.4_shard1:PRIMARY> db.test.find().sort({a:1, b:-1}).hint({a:1, b:-1}).explain("executionStats"){"executionStats" : {"totalKeysExamined" : 15,"totalDocsExamined" : 15,"inputStage" : {"stage" : "FETCH",......"inputStage" : {"stage" : "IXSCAN",......"indexName" : "a_1_b_-1",}}}},}
//多字段等值查询+多字段排序查询 db.test.find({ "a" : 3, "b" : 1}).sort({c:-1, d:1}){ "a" : 3, "b" : 1}等值查询假设a区分度比b高,则对应最优索引为:{a:1, b:1}
{ c:-1, d:1}排序类查询最优索引保持正反序一致,也就是:{ c:-1, d:1}
//非最优索引执行计划,存在内存排序MongoDB_4.4_shard1: PRIMARY>MongoDB_4.4_shard1: PRIMARY> db.test. find({ "a" : 3, "b" : 1}). sort({ c:- 1, d: 1}).hint({a: 1, b: 1, c: 1, d: 1}).explain( "executionStats"){"executionStats" : {......"executionStages" : {"stage" : "FETCH",......"inputStage" : {"stage" : "SORT",......"inputStage" : {"stage" : "IXSCAN","indexName" : "a_1_b_1_c_1_d_1",......}}}},}//最优索引执行计划,直接走排序索引MongoDB_4.4_shard1: PRIMARY>MongoDB_4.4_shard1: PRIMARY> db.test. find({ "a" : 3, "b" : 1}). sort({ c:- 1, d: 1}).hint({a: 1, b: 1, c:- 1, d: 1}).explain( "executionStats"){"executionStats" : {......"executionStages" : {"stage" : "FETCH",......."inputStage" : {"stage" : "IXSCAN",......"indexName" : "a_1_b_1_c_-1_d_1",......}}},}
//等值+非等值+sort排序查询 db.test.find({"a":3, "b":1, "c":{$gte:1}}).sort({d:-1, e:1})//走部分索引,然后内存排序MongoDB_4.4_shard1:PRIMARY> db.test.find({"a":3, "b":1, "c":{$gte:1}}).sort({d:-1, e:1}).hint({"a":1, b:1, c:1, d:-1, e:1}).explain("executionStats"){"executionStats" : {"totalKeysExamined" : 9,"totalDocsExamined" : 9,......"executionStages" : {"stage" : "FETCH",......"inputStage" : {"stage" : "SORT", //内存排序......"inputStage" : {"stage" : "IXSCAN",......"indexName" : "a_1_b_1_c_1_d_-1_e_1",}}}},}//直接走排序索引MongoDB_4.4_shard1:PRIMARY> db.test.find({"a":3, "b":1, "c":{$gte:1}}).sort({d:-1, e:1}).hint({"a":1, b:1, d:-1, e:1, c:1}).explain("executionStats"){"executionStats" : {"totalKeysExamined" : 10,"totalDocsExamined" : 9,......"executionStages" : {"stage" : "FETCH",......"inputStage" : {"stage" : "IXSCAN","indexName" : "a_1_b_1_d_-1_e_1_c_1",......}}},}
//or+sort组合 查询1 db.test.find( { $or: [{ b: 0, d:0 }, {"c":1, "a":6} ] } ).sort({e:-1}) //查询1等价转换为如下查询 -----db.test.find({ b: 3, d:5 }).sort({e:-1}) //查询2 or--| -----db.test.find( {"c":1, "a":6} ).sort({e:-1}) //查询3//走{b:1, d:1, c:1, a:1, e:-1}索引,全表扫描加内存排序MongoDB_4.4_shard1: PRIMARY>MongoDB_4.4_shard1: PRIMARY> db.test. find( { $or: [{ b: 0, d: 0 }, { "c": 1, "a": 6} ] } ). sort({e:- 1}).hint({b: 1, d: 1, c: 1, a: 1, e:- 1}).explain( "executionStats"){"executionStats" : {......//测试构造表中23条数据,总数据23条"totalKeysExamined" : 23,"totalDocsExamined" : 23,"executionStages" : {"stage" : "SORT",......"inputStage" : {"stage" : "FETCH",......"inputStage" : {"stage" : "IXSCAN","indexName" : "b_1_d_1_c_1_a_1_e_-1",......}}}},}//走{b:1, d:1, e:-1}和{c:1, a:1, e:-1}两个最优索引的执行计划,无内存排序MongoDB_4.4_shard1: PRIMARY>MongoDB_4.4_shard1: PRIMARY> db.test. find( { $or: [{ b: 0, d: 0 }, { "c": 1, "a": 6} ] } ). sort({e:- 1}).explain( "executionStats"){"executionStats" : {......"totalKeysExamined" : 2,"totalDocsExamined" : 2,"inputStage" : {"stage" : "FETCH",......"inputStage" : {"stage" : "SORT_MERGE","inputStages" : [{"stage" : "IXSCAN","indexName" : "b_1_d_1_e_1",......},{"stage" : "IXSCAN","indexName" : "c_1_a_1_e_1",......}]}}}},}
//原查询 db.test.find( {"f":3, g:2, $or: [{ b: 0, d:0 }, {"c":1, "a":6} ] } ).sort({e:-1}) //查询1 //拆分后查询 ------ db.test.find( {"f":3, g:2, b: 0, d:0} ).sort({e:-1}) //查询2 or--- ------ db.test.find( {"f":3, g:2, "c":1, "a":6}).sort({e:-1}) //查询3
db.xxx.aggregate({"$indexStats":{}}) { "alxxxId" : 1, "state" : -1, "updateTime" : -1, "itxxxId" : -1, "persxxal" : 1, "srcItxxxId" : -1 } "ops" : NumberLong(88518502) { "alxxxId" : 1, "image" : 1 } "ops" : NumberLong(293104) { "itexxxList.vidxxCheck" : 1, "itemType" : 1, "state" : 1 } "ops" : NumberLong(0) { "alxxxId" : 1, "state" : -1, "newsendTime" : -1, "itxxxId" : -1, "persxxal" : 1 } "ops" : NumberLong(33361216) { "_id" : 1 } "ops" : NumberLong(3987) { "alxxxId" : 1, "createTime" : 1, "checkStatus" : 1 } "ops" : NumberLong(20042796) { "alxxxId" : 1, "parentItxxxId" : -1, "state" : -1, "updateTime" : -1, "persxxal" : 1, "srcItxxxId" : -1 } "ops" : NumberLong(43042796) { "alxxxId" : 1, "state" : -1, "parentItxxxId" : 1, "updateTime" : -1, "persxxal" : -1 } "ops" : NumberLong(3042796) { "itxxxId" : -1} "ops" : NumberLong(38854593) { "srcItxxxId" : -1 } "ops" : NumberLong(0) { "createTime" : 1 } "ops" : NumberLong(62) { "itexxxList.boyunState" : -1, "itexxxList.wozhituUploadServerId" : -1, "itexxxList.photoQiniuUrl" : 1, "itexxxList.sourceType" : 1 } "ops" : NumberLong(0) { "alxxxId" : 1, "state" : 1, "digitalxxxrmarkId" : 1, "updateTime" : -1 } "ops" : NumberLong(140238342) { "itxxxId" : -1 } "ops" : NumberLong(38854593) { "alxxxId" : 1, "parentItxxxId" : 1, "parentAlxxxId" : 1, "state" : 1 } "ops" : NumberLong(132237254) { "alxxxId" : 1, "videoCover" : 1 } { "ops" : NumberLong(2921857) { "alxxxId" : 1, "itemType" : 1 } { "ops" : NumberLong(457) { "alxxxId" : 1, "state" : -1, "itemType" : 1, "persxxal" : 1, " itxxxId " : 1 } "ops" : NumberLong(68730734) { "alxxxId" : 1, "itxxxId" : 1 } "ops" : NumberLong(232360252) { "itxxxId" : 1, "alxxxId" : 1 } "ops" : NumberLong(145640252) { "alxxxId" : 1, "parentAlxxxId" : 1, "state" : 1 } "ops" : NumberLong(689891) { "alxxxId" : 1, "itemTagList" : 1 } "ops" : NumberLong(2898693682) { "itexxxList.photoQiniuUrl" : 1, "itexxxList.boyunState" : 1, "itexxxList.sourceType" : 1, "itexxxList.wozhituUploadServerId" : 1 } "ops" : NumberLong(511303207) { "alxxxId" : 1, "parentItxxxId" : 1, "state" : 1 } "ops" : NumberLong(0) { "alxxxId" : 1, "parentItxxxId" : 1, "updateTime" : 1 } "ops" : NumberLong(0) { "updateTime" : 1 } "ops" : NumberLong(1397) { "itemPhoxxIdList" : -1 } "ops" : NumberLong(0) { "alxxxId" : 1, "state" : -1, "isTop" : 1 } "ops" : NumberLong(213305) { "alxxxId" : 1, "state" : 1, "itemResxxxIdList" : 1, "updateTime" : 1 } "ops" : NumberLong(2591780) { "alxxxId" : 1, "state" : 1, "itexxxList.photoQiniuUrl" : 1} "ops" : NumberLong(23505) { "itexxxList.qiniuStatus" : 1, "itexxxList.photoNetUrl" : 1, "itexxxList.photoQiniuUrl" : 1 } "ops" : NumberLong(0) { "itemResxxxIdList" : 1 } "ops" :NumberLong(7) > db.xxxxx.aggregate({"$indexStats":{}}) { "name" : "alxxxId_1_parentItxxxId_1_parentAlxxxId_1", "key" : { "alxxxId" : 1, "parentItxxxId" : 1, "parentAlxxxId" : 1 }, "host" : "TENCENT64.site:7014", "accesses" : { "ops" : NumberLong(11236765), "since" : ISODate("2020-08-17T06:39:43.840Z") } }|
|
|
|
|
|
|
|
|
4 ►
MongoDB不同分类 查询最优 索引总结
|
|
|
|
|
|
|
|
|
|
| |
|
|
| |
|
|
| |
|
|
|
|
|
|
| |
|
|
|
|
|
|
| |
|
|
|
|
感谢大家一路来对 MongoDB 中文社区的关注与支持,为了让大家有更好的环境交流学习,诚意邀请大家“ 扫描二维码 ”填写问卷,你们的想法对我们非常重要!我们会在参加此次问卷中抽选20名认真填写的用户送上社区专属马克杯!!! 快来参加吧!
长按二维码加入我们
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69961190/viewspace-2886288/ ,如需转载,请注明出处,否则将追究法律责任。