索引
1,explain
explain 会帮助你获得查询方面诸多有用的信息。只要对游标调用该方法,就可以得到查询细节。explain 会返回一个文档,而不是游标本身
> use hello switched to db hello > db.user.find().explain("executionStats") { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "hello.user", "indexFilterSet" : false, "parsedQuery" : { }, "winningPlan" : { "stage" : "COLLSCAN", "direction" : "forward" }, "rejectedPlans" : [ ] }, "executionStats" : { "executionSuccess" : true, "nReturned" : 20, "executionTimeMillis" : 2, "totalKeysExamined" : 0, "totalDocsExamined" : 20, "executionStages" : { "stage" : "COLLSCAN", "nReturned" : 20, "executionTimeMillisEstimate" : 0, "works" : 22, "advanced" : 20, "needTime" : 1, "needYield" : 0, "saveState" : 0, "restoreState" : 0, "isEOF" : 1, "invalidates" : 0, "direction" : "forward", "docsExamined" : 20 } }, "serverInfo" : { "host" : "LAPTOP-A4ECMU55", "port" : 27017, "version" : "4.0.16", "gitVersion" : "2a5433168a53044cb6b4fa8083e4cfd7ba142221" }, "ok" : 1 } >
explain 会返回查询使用的索引情况,耗时和扫描文档数的统计信息。
explain.executionStats.executionTimeMillis
2,MongoDB 索引:
索引是对数据库表中一列或多列的值进行排序的一种结构,可以让我们查询数据库变得更快。MongoDB 的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的查询优化技巧。
①获取当前集合的索引:
> db.user.getIndexes() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "hello.user" } ] >
②创建索引:
> db.user.ensureIndex({"name":1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } > db.user.getIndexes() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "hello.user" }, { "v" : 2, "key" : { "name" : 1 }, "name" : "name_1", "ns" : "hello.user" } ] >
③删除索引:
> db.user.dropIndex({"name":1}) { "nIndexesWas" : 2, "ok" : 1 } > db.user.getIndexes() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "hello.user" } ] >
3,复合索引:
数字1 表示username 键的索引按升序存储,-1 表示age 键的索引按照降序方式存储。
> db.user.ensureIndex({"name":1,"age":-1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } > db.user.getIndexes() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "hello.user" }, { "v" : 2, "key" : { "name" : 1, "age" : -1 }, "name" : "name_1_age_-1", "ns" : "hello.user" } ] >
该索引被创建后,基于name 和age 的查询将会用到该索引,或者是基于name的查询也会用到该索引,但是只是基于age 的查询将不会用到该复合索引。
因此可以说,如果想用到复合索引,必须在查询条件中包含复合索引中的前N 个索引列。
然而如果查询条件中的键值顺序和复合索引中的创建顺序不一致的话,MongoDB 可以智能的帮助我们调整该顺序,以便使复合索引可以为查询所用
> db.user.find({"age":16,"name":"jack11"}) { "_id" : ObjectId("5eeb59f89b5a0b09e5f8c2aa"), "ID" : 11, "name" : "jack11", "age" : 16 } >
对于上面示例中的查询条件,MongoDB 在检索之前将会动态的调整查询条件文档的顺序,以使该查询可以用到刚刚创建的复合索引。
对于上面创建的索引,MongoDB 都会根据索引的keyname 和索引方向为新创建的索引自动分配一个索引名,下面的命令可以在创建索引时为其指定索引名,如:
> db.user.ensureIndex({"name":1},{"name":"userindex"}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 2, "numIndexesAfter" : 3, "ok" : 1 } > db.user.getIndexes() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "hello.user" }, { "v" : 2, "key" : { "name" : 1, "age" : -1 }, "name" : "name_1_age_-1", "ns" : "hello.user" }, { "v" : 2, "key" : { "name" : 1 }, "name" : "userindex", "ns" : "hello.user" } ] >
随着集合的增长,需要针对查询中大量的排序做索引。如果没有对索引的键调用sort,MongoDB 需要将所有数据提取到内存并排序。因此在做无索引排序时,如果数据量过大以致无法在内存中进行排序,此时MongoDB 将会报错。
4,唯一索引:
在缺省情况下创建的索引均不是唯一索引。
创建唯一索引:
db.user.ensureIndex({"name":1},{"unique":true})
如果再次插入name 重复的文档时,MongoDB 将报错,以提示插入重复键,如:
db.user.insert({"name":sally})
db.user.insert({"name":sally})
E11000 duplicate key error index: user.user.$name_1 dup key: { : sally }
如果插入的文档中不包含name键,那么该文档中该键的值为null,如果多次插入类似的文档,MongoDB 将会报出同样的错误,如
db.user.insert({"name":sally})
db.user.insert({"name":sally})
如果在创建唯一索引时已经存在了重复项,我们可以通过下面的命令帮助我们在创建唯一索引时消除重复文档,仅保留发现的第一个文档,如
先删除刚刚创建的唯一索引。
db.user.dropIndex({"name":1})
插入测试数据,以保证集合中有重复键存在。
db.user.remove()
db.user.insert({"name":sally})
db.user.insert({"name":sally})
重新创建唯一索引
db.user.ensureIndex({"name":1},{"unique":true })
同样可以创建复合唯一索引,即保证复合键值唯一即可。如:
db.user.ensureIndex({"name":1,"age":1},{"unique":true})
如果在为已有数据的文档创建索引时,可以执行下面的命令,以使MongoDB 在后台创建索引,这样的创建时就不会阻塞其他操作。但是相比而言,以阻塞方式创建索引,会使整个创建过程效率更高,但是在创建时MongoDB 将无法接收其他的操作。
db.user.ensureIndex({"name":1},{"background":true})