在1个键上创建的索引就是单键索引,单键索引是最多见的索引,如MongoDB默许创建的_id的索引就是单键索引。
在多个键上建立的索引就是复合索引
如果在1个值为数组的字段上面创建索引, MongoDB会自己决定,是不是要把这个索引建成多键索引
MongoDB支持几种类型的地理空间索引。其中最经常使用的是 2dsphere 索引(用于地球表面类型的地图)和 2d 索引(用于平面地图和时间连续的数据)
全文索援用于在文档中搜索文本,我们也能够使用正则表达式来查询字符串,但是当文本块比较大的时候,正则表达式搜索会非常慢,而且没法处理语言理解的问题(如 entry 和 entries 应当算是匹配的)。使用全文索引可以非常快地进行文本搜索,就犹如内置了多种语言分词机制的支持1样。创建索引的开消都比较大,全文索引的开消更大。创建索引时,需后台或离线创建
哈希 索引可以支持相等查询,但是 哈希 索引不支持范围查询。您可能没法创建1个带有 哈希 索引键的复合索引或对 哈希 索引施加唯1性的限制。但是,您可以在同1个键上同时创建1个 哈希 索引和1个递增/递减(例如,非哈希)的索引,这样MongoDB对范围查询就会自动使用非哈希的索引
- 唯1索引可以谢绝保存那些被索引键的值已重复的文档。
- 默许情况下,MongoDB索引的 unique 属性是 false 。如果对复合索引施加唯1性的限制,那末MongoDB就会强迫要求 复合 值的唯1性,而不是分别对每一个单独的值要求唯1。
- 唯1性的限制是针对1个集合中不同文档的。也即,唯1索引可以避免 不同 文档的被索引键上存储相同值,但是它不由止同1篇文档在被索引键存储的数组里存储的元素或内嵌文档是相同的值。
- 在同1篇文档存储重复数据的情况下,重复的值只会被存入索引1次。
- 如果1篇文档不包括唯1索引的被索引键,那末索引默许会为该文档存储1个null值。由于唯1性的限制,MongoDB将只允许有1篇可以不包括被索引键。如果超过1篇文档不包括被索引键或没有值,那末会抛出键重复(duplicate key)毛病致使索引创建失败。可以组合使用唯1性和稀疏索引的特性来过滤那些包括null值的文档以免这个毛病。
- 稀疏索引会跳过所有不包括被索引键的文档。这个索引之所以称为 “稀疏” 是由于它其实不包括集合中的所有文档。与之相反,非稀疏的索引会索引每篇文档,如果1篇文档不含被索引键则为它存储1个null值。
- 如果1个索引会致使查询或排序的结果集是不完全的,那末MongoDB将不会使用这个索引,除非用户使用 hint() 方法来显示指定索引。例如,查询 { x: { $exists: false } } 将不会使用 x 键上的稀疏索引,除非显示的hint。
- 2dsphere (version 2), 2d 和 text 这些索引总是稀疏的。
- 只要1个文档里有最少1个被索引键,稀疏且只包括有递增/递减索引键的复合索引就会索引这篇文档。
- 至于稀疏且包括有地理索引键(例如 2dsphere, 2d)和递增/递减索引键的复合索引,只有地理索引键的存在与否能决定1篇文档是不是被索引。
- 至于稀疏且包括了全文索引键和其他递增/递减索引键的复合索引,只有全文索引键的存在与否能决定是不是索引该文档。
- 1个稀疏且唯1的索引,可以避免集合中的文档被索引键中出现重复值,同时也允许多个文档里不包括被索引键
备注:3.2.0 版本后推荐使用Partial Indexes
- 这是3.2.0版本以后新增的
- 只会对Collections满足条件partialFilterExpression的文档进行索引
- 使用该索引,会在1定程度上减少存储空间和创建索引和保护性能下降的本钱
- TTL 集合支持失效时间设置,当超过指定时间后,集合自动清除超时的文档,这用来保存1些诸如session会话信息的时候非常有用,或存储缓存数据使用。但删除会有延时
- 索引的字段必须是1个日期的 bson 类型
- 你不能创建 TTL 索引,如果要索引的字段已在其他索引中使用。否则超时后文档不会被自动清除。
- 索引不能包括多个字段
参数名 | 类型 | 描写 |
---|---|---|
background | Boolean | 建索引进程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 “background” 可选参数。 “background” 默许值为false。 |
unique | Boolean | 建立的索引是不是唯1。指定为true创建唯1索引。默许值为false. |
name | string | 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成1个索引名称。不能超过128字符 |
partialFilterExpression | Document | 设定索引只对满足条件的文档起作用 具体见官网对Partial Indexes |
sparse | Boolean | 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包括对应字段的文档.。默许值为 false. |
expireAfterSeconds | integer | 指定1个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。 |
storageEngine | Document | 允许用户在创建索引时指定每个索引的配置 |
参数名 | 类型 | 描写 |
---|---|---|
weights | document | 设置文本索引字段的权重,权重值1- 99,999 |
default_language | 设置文本分词的语言,默许为english,其他支持语言 | |
language_override | string | 使用文档中的1个字段的值作为设置文本分词的语言,默许为language,例子 |
textIndexVersion | integer | 版本号,可以是1或2 |
备注:其他索引属性见官网
语法:db.collection.getIndexes()
示例:
db.index_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 标识索引升序,⑴降序
2. options : 设置索引选项,如设置名称、设置成为唯1索引
准备数据
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或⑴},{options})
示例:
db.index_test.createIndex({"age":1})
履行成功后可以看到返回的numIndexesAfter比numIndexesBefore大
示例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大,说明在查询的时候进行了1些没有必要的扫描。
示例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:文档扫描条目)
总结:在设置查询字段时应尽可能只设置建立了索引的字段
示例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()
结果与上面1样
示例3:排序字段只包括1个单个索引字段
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即索引在排序中没有起到作用,这说明单键索引在多个字段排序时没有作用。
总结:
- 排序操作可以通过从索引中依照索引顺序获得文档的方式来保证结果的有序性。如果查询计划器(planner)没法从索引中得到排序顺序,那末它将需要在内存中排序(winningPlan.stage=SORT)结果。
- 在多个字段上做排序时需要使用复合索引
语法: db.collections.createIndex({“字段名1”:1或⑴,…,”字段名n”:1或⑴},{options})
示例:
db.index_test.dropIndexes() //先删除原来创建的索引
db.index_test.createIndex({"age":1,"sex":1}) //在age和sex上创建复合索引
创建成功后,通过db.index_test.getIndexes() 可看到创建的复合索引其实只是1个索引,其key是{“age” : 1,”sex” : 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()
可以看到第1句履行返回的winningPlan.stage=FETCH;且winningPlan.inputStage.stage=IXSCAN;indexName=age_⑴_sex_1
而第2句履行返回的wininigPlan.stage=COLLSCAN
这好像说明查询条件只是复合索引key中部份字段时,索引只对创建时指定的第1个字段有作用。接下来我们先删除原本的索引,然后在age,name,sex3个字段上创建1个复合索引再来看看。
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”
示例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
这说明排序字段只包括创建复合索引字段中的部份字段时排序键的顺序必须和它们在索引中的排列顺序 1致,且不能跳跃(即第1个字段必须有,且不能跳过中间的字段)接下来再看下面的查询情况
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中指定的所有键的排序顺序(例如递增/递减)必须和索引中的对应键的排序顺序 完全相同, 或 完全相反
总结
可以指定在索引的所有键或部份键上排序。但是,排序键的顺序必须和它们在索引中的排列顺序 1致
sort中指定的所有键的排序顺序(例如递增/递减)必须和索引中的对应键的排序顺序 完全相同, 或 完全相反
语法:db.collections.createIndex({“字段名”: 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索引会禁止写入在唯1索引字段上重复的数据
总结1:
唯1索引会禁止利用插入被索引键上的值是重复值的 documents
不能再已有重复数据的字段上建立唯1索引
语法:db.collections.createIndex({“字段名”: 1或⑴,…”字段名n”: 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索引会强迫要求 复合 键值的唯1性,而 不是 每一个键的唯1性。
总结:
- 唯1索引会禁止利用插入被索引键上的值是重复值的 documents
- 不能再已有重复数据的字段上建立唯1索引
- 在多字段上建唯1索引会强迫要求 复合 键值的唯1性,而 不是 每一个键的唯1性
语法: db.collection.createIndex({“字段名”:1},{“sparse”:true})
在1个字段上创建稀疏索引,索引会跳过所有不包括被索引键(字段)的文档
在履行查询 { 被索引键:{$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(使用索引扫描);
最后1条履行 exists:true 查询,没有显示指定hint,其返回的winningPlan.stage=FETCH(使用索引扫描);
总结:
在某1个被创建了稀疏索引字段上履行exists:false查询时,需要显示指定hint,其索引才会起作用;而履行 exists:true查询时,则不需要。
在字段上创建普通索引 ,如果文档不含该字段这其索引值会被设为null,而稀疏索引会跳过该文档;这就是说使用该索引扫描集合时稀疏索引会比普通索引少。
语法: db.collection.createIndex({“字段名”: 1或⑴,…”字段名n”: 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})
如果希望修改1条索引,您需要删除然后重建它
如果您需要重建集合中的索引,您可使用 db.collection.reIndex() ,1条操作就能够重建这个集合上的所有索引。它会删除所有索引,包括 _id 索引 ,然后重建所有索引。
(备注:其他索引在本人工作中很少使用过,这里就不描写了)
索引策略
索引创建教程