当前位置:Gxlcms > 数据库问题 > MongoDB的学习(3)--索引

MongoDB的学习(3)--索引

时间:2021-07-01 10:21:17 帮助过:4人阅读

(i = 0; i < 1000000; i++) { db.users.insert({ "i": i, "username": "user" + i, "age": Math.floor(Math.random() * 120), "created": new Date() }); }

数据库中会创建一百万条数据,稍微有点慢,需要等会。

我们可以使用explain()函数查看MongoDB在执行查询的过程中所做的事情。执行如下命令,查找用户名为user1000的用户。

db.users.find({username:"user1000"}).explain()

得到结果如下:

{
    "cursor" : "BasicCursor",
    "isMultiKey" : false,
    "n" : 1,
    "nscannedObjects" : 1000000,
    "nscanned" : 1000000,
    "nscannedObjectsAllPlans" : 1000000,
    "nscannedAllPlans" : 1000000,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 7813,
    "nChunkSkips" : 0,
    "millis" : 411,
    "server" : "user:27017",
    "filterSet" : false
}

之后会详细介绍各个字段的意思,现在我们只需要知道,"n"表示查询结果的数量,"nscanned"表示MongoDB在完成这个查询的过程中扫描的文件总数,"millis"表示这个查询耗费的毫秒数。可以看到,为了查找user1000,MongoDB遍历了整个集合,消耗了411毫秒。

为了优化查询,我们可以在查找到一个结果的时候,就结束查询,返回结果。命令如下:

db.users.find({username:"user1000"}).limit(1).explain()

结果如下:

{
    "cursor" : "BasicCursor",
    "isMultiKey" : false,
    "n" : 1,
    "nscannedObjects" : 1001,
    "nscanned" : 1001,
    "nscannedObjectsAllPlans" : 1001,
    "nscannedAllPlans" : 1001,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 7,
    "nChunkSkips" : 0,
    "millis" : 1,
    "server" : "user:27017",
    "filterSet" : false
}

可以看到扫描文档数和消耗时间都变少了很多,但是如果我们要查找user999999,MongoDB还是要遍历集合才能找到。而且随着用户数量的增多,查询会越来越慢。

对于这种情况,创建索引是一个非常好的解决方案:索引可以根据给定的字段组织数据,让MongoDB能够非常快速的找到目标文档。使用如下命令,在username字段上创建一个索引。

db.users.ensureIndex({"username":1})

然后再来执行一下之前执行过的语句

db.users.find({username:"user1000"}).explain()

其结果如下:

{
    "cursor" : "BtreeCursor username_1",
    "isMultiKey" : false,
    "n" : 1,
    "nscannedObjects" : 1,
    "nscanned" : 1,
    "nscannedObjectsAllPlans" : 1,
    "nscannedAllPlans" : 1,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    "indexBounds" : {
        "username" : [
            [
                "user1000",
                "user1000"
            ]
        ]
    },
    "server" : "user:27017",
    "filterSet" : false
}

然后你会发现查询变快了很多,几乎是瞬间完成,这就是使用索引的效果。但是索引也是有代价的,对于添加的每一个索引,每次写操作(插入、更新、删除)都将耗费更多的时间。这是因为当数据发生变动时,MongoDB不仅要更新文档,还要更新集合上的所有索引。因此,MongoDB限制每个集合上最多只能有64个索引。通常,在一个特定的集合上,不应该拥有两个以上的索引。

 当一个索引建立在多个字段上时,我们称它为复合索引,创建的语句如下:

db.users.ensureIndex({"age":1, "username":1})

 如果查询中有多个排序方向或者查询条件中有多个键,复合索引就会非常有用。

MongoDB对这个索引的使用方法取决于查询的类型。下面是三种主要的方式。

第一种:

db.users.find({"age":21}).sort({"username":-1})

这是一个点查询,用于查找单个值(尽管包含这个值的文档是多个)。由于索引中的第二个字段,查询结果已经是有序的了。这种类型的查询是非常高效的。

第二种:

db.users.find({"age":{"$gte":21,"$lte":30}})

这是一个多值查询,查找到多个值相匹配的文档,MongoDB会使用索引中的第一个键“age”得到匹配文档。如果使用“username”做查询,该索引不起作用。

第三种:

db.users.find({"age":{"$gte":21,"$lte":30}}).sort({"username":1})

这也是一个多值查询,与上一个类似,只是这次需要对查询结果进行排序。MongoDB需要在内存中对结果进行排序,不如上一个高效。

删除索引的命令如下:

db.users.dropIndex(‘age_1_username_1‘)

删除users集合中名字为‘age_1_username_1‘的索引。

所有数据库的索引信息都存储在system.indexes集合中,这是一个保留集合,不能在其中插入或者删除文档,只能通过ensureIndex和dropIndex对其进行操作。

使用如下命令可以获取users集合上的索引信息:

db.users.getIndexes()

结果如下:

[
    {
        "v" : 1,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_",
        "ns" : "test.users"
    },
    {
        "v" : 1,
        "key" : {
            "username" : 1
        },
        "name" : "username_1",
        "ns" : "test.users"
    },
    {
        "v" : 1,
        "key" : {
            "age" : 1,
            "username" : 1
        },
        "name" : "age_1_username_1",
        "ns" : "test.users"
    }
]

 

MongoDB的学习(3)--索引

标签:

人气教程排行