当前位置:Gxlcms > 数据库问题 > mongodb3.0的索引管理学习整理

mongodb3.0的索引管理学习整理

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

_test.getIndexes() 输出: [ { "v" : 1, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "usercenter_test.index_test" } ]

可以看出_id字段是默认建立了索引的

五、建立索引

从3.0版本后使用 db.collection.createIndex()代替db.collection.ensureIndex()

语法:db.collection.createIndex(keys, options)

参数说明:
1. keys: {字段名1:ascending,… 字段名n:ascending}: ascending 设为1 标识索引升序,-1降序
2. options : 设置索引选项,如设置名称、设置成为唯一索引

准备数据

db.index_test.insert({"name":"2","age":53,"sex":1})
db.index_test.insert({"name":"3","age":19,"sex":0})
db.index_test.insert({"name":"4","age":20,"sex":2})
db.index_test.insert({"name":"5","age":63,"sex":5})
db.index_test.insert({"name":"6","age":18,"sex":0})
db.index_test.insert({"name":"7","age":98,"sex":1})
db.index_test.insert({"name":"8","age":76,"sex":1})
db.index_test.insert({"name":"9","age":7,"sex":2})
db.index_test.insert({"name":"l0","age":15,"sex":1})
db.index_test.insert({"name":"l","age":23,"sex":1})

(一)、 单一键上创建普通索引

语法: db.collections.createIndex({“字段名”:1或-1},{options})

示例:
db.index_test.createIndex({"age":1})

执行成功后可以看到返回的numIndexesAfter比numIndexesBefore大

1、单键索引与查询字段设置对查询性能的影响

示例1:查询字段不包含索引字段

db.index_test.find({"sex":1}).explain("executionStats")

可以看到其 winningPlan.stage=COLLSCAN是全表扫描

示例2:查询字段同时包含索引字段和非索引字段

db.index_test.find({"age":{"$gte":10},"sex":1}).explain("executionStats")

虽然 winningPlan.stage=FETCH以及winningPlan.inputStage.stage =IXSCAN,但是其totalKeysExamined和totalDocsExamined都比nReturned大,说明在查询的时候进行了一些没有必要的扫描。

示例3:查询字段同时只包含索引字段

db.index_test.find({"age":{"$gte":10}}).explain("executionStats")

可以看到返回中的

winningPlan.stage=FETCH(根据索引去检索指定document )
winningPlan.inputStage.stage =IXSCAN(索引扫描)
executionStats.nReturned=totalKeysExamined=totalDocsExamined=9表示该查询使用的根据索引去查询指定文档
(nReturned:查询返回的条目,totalKeysExamined:索引扫描条目,totalDocsExamined:文档扫描条目)

总结:在设置查询字段时应尽量只设置建立了索引的字段

2、单键索引与查询时排序字段的设置对查询性能的影响

示例1:排序字段不包含索引字段

 db.index_test.find({"age":20}).sort({"sex":-1}).explain()

返回中的winningPlan.stage=SORT 即查询后需要在内存中排序再返回

示例2:排序字段同时包含索引字段和非索引字段

db.index_test.find({"age":20}).sort({"age":1,"sex":1}).explain()

结果与上面一样

示例3:排序字段只包含一个单个索引字段

db.index_test.find({"age":20}).sort({"age":1}).explain()

可以看到winningPlan.stage变为了FETCH(使用索引)

示例4:排序字段包含多个单个索引字段

db.index_test.find({}).sort({"sex":1,"age":1}).explain("executionStats")
db.index_test.find({}).sort({"age":1,"sex":1).explain("executionStats")

可以看到这种情况winningPlan.stage为sort即索引在排序中没有起到作用,这说明单键索引在多个字段排序时没有作用。

总结:

  1. 排序操作可以通过从索引中按照索引顺序获取文档的方式来保证结果的有序性。如果查询计划器(planner)无法从索引中得到排序顺序,那么它将需要在内存中排序(winningPlan.stage=SORT)结果。
  2. 在多个字段上做排序时需要使用复合索引

(二)、创建复合索引

语法: db.collections.createIndex({“字段名1”:1或-1,…,”字段名n”:1或-1},{options})

示例:
db.index_test.dropIndexes() //先删除原来创建的索引
db.index_test.createIndex({"age":1,"sex":1}) //在age和sex上创建复合索引

创建成功后,通过db.index_test.getIndexes() 可看到创建的复合索引其实只是一个索引,其key是{“age” : 1,”sex” : 1};而不是多个。

1. 复合索引与查询字段设置对查询性能的影响

示例1:查询字段只包含创建复合索引字段中的部分字段

db.index_test.dropIndexes() //先删除原来创建的索引
db.index_test.createIndex({"age":1,"sex":1}) //在age和sex上创建复合索引

然后分别执行

db.index_test.find({"age":1}).explain()
db.index_test.find({"sex":1}).explain()

可以看到第一句执行返回的winningPlan.stage=FETCH;且winningPlan.inputStage.stage=IXSCAN;indexName=age_-1_sex_1

而第二句执行返回的wininigPlan.stage=COLLSCAN

这好像说明查询条件只是复合索引key中部分字段时,索引只对创建时指定的第一个字段有作用。接下来我们先删除原有的索引,然后在age,name,sex三个字段上创建一个复合索引再来看看。

db.index_test.dropIndexes()
db.index_test.createIndex({"age":-1,"name":1,"sex":1})
db.index_test.find({"age":"1"}).explain() //第1条查询
db.index_test.find({"sex":"1"}).explain() //第2条查询
db.index_test.find({"age":"1","sex":1}).explain()//第3条查询
db.index_test.find({"name":"1","sex":1}).explain()//第4条查询

可以看到第1条以及第3条查询返回的都是winningPlan.stage=FETCH;而第2条和第4条查询返回的都是winningPlan.stage=COLLSCAN

总结:

查询条件只是复合索引key中部分字段时,如果想要复合索引起到优化的作用则必须包含创建复合索引时指定的第1个字段;如上面的示列中的字段”age”

2. 复合索引与排序字段设置对查询性能的影响

示例1:排序字段只包含创建复合索引字段中的部分字段

db.index_test.find().sort({"age":-1}).explain()
db.index_test.find().sort({"age":1}).explain()
db.index_test.find().sort({"name":1}).explain()
db.index_test.find().sort({"sex":1}).explain()
db.index_test.find().sort({"age":-1,"name":1}).explain()
db.index_test.find().sort({"name":1,"sex":1}).explain()
db.index_test.find().sort({"age":-1,"sex":1}).explain()

上面的只有第1,2,5 返回的winningPlan.stage= FETCH ;其他都是=sort
这说明排序字段只包含创建复合索引字段中的部分字段时排序键的顺序必须和它们在索引中的排列顺序 一致,且不能跳跃(即第一个字段必须有,且不能跳过中间的字段)

接下来再看下面的查询情况

db.index_test.find().sort({"age":1,"name":-1}).explain()
db.index_test.find().sort({"age":-1,"name":-1}).explain()

第1条返回的winningPlan.stage= FETCH;而第2条winningPlan.stage= SORT;这说明sort中指定的所有键的排序顺序(例如递增/递减)必须和索引中的对应键的排序顺序 完全相同, 或者 完全相反

总结

可以指定在索引的所有键或者部分键上排序。但是,排序键的顺序必须和它们在索引中的排列顺序 一致

sort中指定的所有键的排序顺序(例如递增/递减)必须和索引中的对应键的排序顺序 完全相同, 或者 完全相反

(三)、创建唯一索引

1. 单字段上建唯一索引

语法:db.collections.createIndex({“字段名”: 1或-1},{“unique”:true})
示例1:

db.index_test.createIndex({"name":1},{"unique":true})
db.index_test.createIndex({"sex":1},{"unique":true})

可以看到第1条执行成功,而第2条则失败因为已存在的数据在sex字段上有重复的数据。

示例2:

db.index_test.insert({"name":"2","age":123,"sex":9})
db.index_test.insert({"name":"22","age":123,"sex":9})

可以看到第1条执行失败,第2条成功。因为唯一索引会阻止写入在唯一索引字段上重复的数据

总结1:

唯一索引会阻止应用插入被索引键上的值是重复值的 documents
不能再已经有重复数据的字段上建立唯一索引

2. 多字段上建唯一索引

语法:db.collections.createIndex({“字段名”: 1或-1,…”字段名n”: 1或-1},{“unique”:true})

db.index_test.createIndex({"name":1,"age":1},{"unique":true}
db.index_test.insert({"name":"22","age":123,"sex":1})
db.index_test.insert({"name":"23","age":123,"sex":1})
db.index_test.insert({"name":"22","age":123,"sex":2})

可以看到第2条和第3条都能成功,但最后1条却是失败的。这说明在多字段上建唯一索引会强制要求 复合 键值的唯一性,而 不是 每个键的唯一性。

总结:

  1. 唯一索引会阻止应用插入被索引键上的值是重复值的 documents
  2. 不能再已经有重复数据的字段上建立唯一索引
  3. 在多字段上建唯一索引会强制要求 复合 键值的唯一性,而 不是 每个键的唯一性

(四)、创建稀疏索引

语法: db.collection.createIndex({“字段名”:1},{“sparse”:true})

1. 稀疏索引与$exists的关系

在一个字段上创建稀疏索引,索引会跳过所有不包含被索引键(字段)的文档

在执行查询 { 被索引键:{$exists:false}},不会使用该稀疏索引,除非显示指定hint({被索引键:1})

示例:
db.scores.insert({"userid":"lxh"})
db.scores.insert({"userid":"pen","score":89})
db.scores.insert({"userid":"xiao","score":90})
//在字段score上创建稀疏索引
db.scores.createIndex( { score: 1 } , {sparse:true} )
db.scores.find( { score:{$exists:false}}).explain()
db.scores.find( { score:{$exists:false}}).hint({score:1}).explain()
db.scores.find( { score:{$exists:true}}).explain()

从例子可以看出第5条在执行$exists:false 查询时,其返回的winningPlan.stage=COLLSCAN(表示扫描全表);

第5条执行exists:false 查询时,显示指定了hint,其返回的winningPlan.stage=FETCH(使用索引扫描);

最后一条执行 exists:true 查询,没有显示指定hint,其返回的winningPlan.stage=FETCH(使用索引扫描);

总结:

在某一个被创建了稀疏索引字段上执行exists:false查询时,需要显示指定hint,其索引才会起作用;而执行 exists:true查询时,则不需要。

在字段上创建普通索引 ,如果文档不含该字段这其索引值会被设为null,而稀疏索引会跳过该文档;这就是说使用该索引扫描集合时稀疏索引会比普通索引少。

(五)、创建Partial Index

语法: db.collection.createIndex({“字段名”: 1或-1,…”字段名n”: 1或-1},{“partialFilterExpression”:{partialFilterExpression }})

partialFilterExpression表达式如下

equality expressions (i.e. field: value or using the $eq operator)
$exists: true expression,
$gt, $gte, $lt, $lte expressions,
$type expressions,
$and operator at the top-level only
示例:
db.restaurants.createIndex(
   { cuisine: 1, name: 1 },
   { partialFilterExpression: { rating: { $gt: 5 } } }
)

(备注:地理空间索引,全文索引、TTL索引、哈希索引本人工作中很少使用到,这里就没有描述了)

(六)、删除修改索引

删除所有索引

db.collections.dropIndexes()

删除指定索引

db.collections.dropIndex({"被创建索引的字段名":1})

修改索引

如果希望修改一条索引,您需要删除然后重建它

重建索引

如果您需要重建集合中的索引,您可以使用 db.collection.reIndex() ,一条操作就可以重建这个集合上的所有索引。它会删除所有索引,包括 _id 索引 ,然后重建所有索引。

(备注:其他索引在本人工作中很少使用过,这里就不描述了)

参考文章

索引策略

索引创建教程

mongodb3.0的索引管理学习整理

标签:

人气教程排行