标准 专业
多元 极客

MongoDB研究院(2)——索引——复合索引

MongoDB同样支持用户自定义的多个字段组成的索引,称为复合索引。

小贴士

复合索引最大可拥有31个字段。

创建复合索引

创建复合索引的语句格式如下:

db.collection.createIndex( { <field1>: <type>, <field2>: <type2>, ... } )

复合索引中的索引类型与单键索引中的索引类型意义大致相同,但是复合索引不能创建hash索引类型。

接下来我们继续对achievement集合做测试,创建的索引如下所示:

db.achievement.createIndex({"student_name" : 1, "score" : -1})

注意,创建索引时字段的顺序是非常重要的,以上面创建索引为例,先是根据student_name进行升序排列,然后再根据student_name中的score进行降序排列。

除了可以匹配所有的索引字段,复合索引还支持索引字段的子集字段的查询匹配,比如说,下面例子都会通过索引进行查询:

db.achievement.find({"student_name" : "student_85"})
db.achievement.find({"student_name" : "student_85", "score" : 85})

索引排序

在单键索引中,由于MongoDB会从任意一个方向对索引数据进行遍历,所以不必过多担心索引排序的问题。但是在复合索引中,需要care这个问题。

为了得出直观的结果,我们将之前创建的索引暂时清除掉,然后重新创建一个如下的索引:

db.achievement.createIndex({"student_name" : "1", "score" : "-1"})

在进行如下查询时,MongoDB是这样解释的:

> db.achievement.find().sort({"student_name" : 1, "score" : -1}).explain()
{
        ...
                "winningPlan" : {
                        "stage" : "FETCH",
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "student_name" : 1,
                                        "score" : -1
                                },
                                "indexName" : "student_name_1_score_-1",
                                "isMultiKey" : false,
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 1,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "student_name" : [
                                                "[MinKey, MaxKey]"
                                        ],
                                        "score" : [
                                                "[MaxKey, MinKey]"
                                        ]
                                }
                        }
                },
        ...
}

> db.achievement.find().sort({"student_name" : -1, "score" : 1}).explain()
{
        ...
                "winningPlan" : {
                        "stage" : "FETCH",
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "student_name" : 1,
                                        "score" : -1
                                },
                                "indexName" : "student_name_1_score_-1",
                                "isMultiKey" : false,
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 1,
                                "direction" : "backward",
                                "indexBounds" : {
                                        "student_name" : [
                                                "[MaxKey, MinKey]"
                                        ],
                                        "score" : [
                                                "[MinKey, MaxKey]"
                                        ]
                                }
                        }
                },
        ...
}

两个查询条件均通过索引进行遍历。

但是当执行下述查询时:

> db.achievement.find().sort({"student_name" : 1, "score" : 1}).explain()
{
        ...
                "winningPlan" : {
                        "stage" : "SORT",
                        "sortPattern" : {
                                "student_name" : 1,
                                "score" : 1
                        },
                        "inputStage" : {
                                "stage" : "SORT_KEY_GENERATOR",
                                "inputStage" : {
                                        "stage" : "COLLSCAN",
                                        "direction" : "forward"
                                }
                        }
                },
        ...
}

> db.achievement.find().sort({"student_name" : -1, "score" : -1}).explain()
{
        ...
                "winningPlan" : {
                        "stage" : "SORT",
                        "sortPattern" : {
                                "student_name" : -1,
                                "score" : -1
                        },
                        "inputStage" : {
                                "stage" : "SORT_KEY_GENERATOR",
                                "inputStage" : {
                                        "stage" : "COLLSCAN",
                                        "direction" : "forward"
                                }
                        }
                },
        ...
}

发现并没有遍历索引,而是进行了全局扫描,更多的关于遍历索引排序的问题,请查看这篇文章:使用索引进行排序。

索引前缀

索引前缀指的是索引字段开头部分的子集,比如说,我们创建一个如下的索引:

db.achievement.createIndex({"student_name" : 1, "score" : 1, "address.province" : 1})

则下面这些索引都属于上面这个索引的索引前缀:

{"student_name" : 1}
{"student_name" : 1, "score" : 1}

即使用以上三个形式作为查询条件时,都会通过索引进行遍历。但是,如下情况也会通过索引进行遍历:

{"student_name" : 1, "address.province" : 1}

但是它只会命中{“student_name” : 1}这个索引,{“address.province” : 1}这个部分将会对前面遍历出来的文档进行全局遍历,最后得出需要的结果集。

但是,如下几种查询形式,将不会通过索引进行遍历:

{"score" : 1 }
{"address.province" : 1}
{"score" : 1, "address.province" : 1}

小贴士

如果你有一个集合中拥有形如{“a” : 1, “b” : 1}和{“a” : 1}的索引,如果其中的每一个索引都没有稀疏或者唯一的约束,那么完全可以将{“a” : 1}这个索引删除掉,MongoDB完全支持通过索引前缀对集合的遍历。

赞(1) 投币

评论 抢沙发

慕勋的实验室慕勋的研究院

码字不容易,路过请投币

支付宝扫一扫

微信扫一扫