mongodb3.0的索引管理及执行计划学习整理

一、 索引类型

(一)、单键索引

在一个键上创建的索引就是单键索引,单键索引是最常见的索引,如MongoDB默认创建的_id的索引就是单键索引。

(二)、复合索引

在多个键上建立的索引就是复合索引

(三)、多建索引

如果在一个值为数组的字段上面创建索引, MongoDB会自己决定,是否要把这个索引建成多键索引

(四)、地理空间索引

MongoDB支持几种类型的地理空间索引。其中最常用的是 2dsphere 索引(用于地球表面类型的地图)和 2d 索引(用于平面地图和时间连续的数据)

(五)、全文索引

全文索引用于在文档中搜索文本,我们也可以使用正则表达式来查询字符串,但是当文本块比较大的时候,正则表达式搜索会非常慢,而且无法处理语言理解的问题(如 entry 和 entries 应该算是匹配的)。使用全文索引可以非常快地进行文本搜索,就如同内置了多种语言分词机制的支持一样。创建索引的开销都比较大,全文索引的开销更大。创建索引时,需后台或离线创建

(六)、哈希索引

哈希 索引可以支持相等查询,但是 哈希 索引不支持范围查询。您可能无法创建一个带有 哈希 索引键的复合索引或者对 哈希 索引施加唯一性的限制。但是,您可以在同一个键上同时创建一个 哈希 索引和一个递增/递减(例如,非哈希)的索引,这样MongoDB对于范围查询就会自动使用非哈希的索引

二、 索引属性

(一)、唯一索引

  1. 唯一索引可以拒绝保存那些被索引键的值已经重复的文档。
  2. 默认情况下,MongoDB索引的 unique 属性是 false 。如果对复合索引施加唯一性的限制,那么MongoDB就会强制要求 复合 值的唯一性,而不是分别对每个单独的值要求唯一。
  3. 唯一性的限制是针对一个集合中不同文档的。也即,唯一索引可以防止 不同 文档的被索引键上存储相同值,但是它不禁止同一篇文档在被索引键存储的数组里存储的元素或者内嵌文档是相同的值。
  4. 在同一篇文档存储重复数据的情况下,重复的值只会被存入索引一次。
  5. 如果一篇文档不包含唯一索引的被索引键,那么索引默认会为该文档存储一个null值。由于唯一性的限制,MongoDB将只允许有一篇可以不包含被索引键。如果超过一篇文档不包含被索引键或没有值,那么会抛出键重复(duplicate key)错误导致索引创建失败。可以组合使用唯一性和稀疏索引的特性来过滤那些包含null值的文档以避免这个错误。

(二)、稀疏索引(Sparse Indexes)

  1. 稀疏索引会跳过所有不包含被索引键的文档。这个索引之所以称为 “稀疏” 是因为它并不包括集合中的所有文档。与之相反,非稀疏的索引会索引每一篇文档,如果一篇文档不含被索引键则为它存储一个null值。
  2. 如果一个索引会导致查询或者排序的结果集是不完整的,那么MongoDB将不会使用这个索引,除非用户使用 hint() 方法来显示指定索引。例如,查询 { x: { $exists: false } } 将不会使用 x 键上的稀疏索引,除非显示的hint。
  3. 2dsphere (version 2), 2d 和 text 这些索引总是稀疏的。
  4. 只要一个文档里有至少一个被索引键,稀疏且只包含有递增/递减索引键的复合索引就会索引这篇文档。
  5. 至于稀疏且包含有地理索引键(例如 2dsphere, 2d)以及递增/递减索引键的复合索引,只有地理索引键的存在与否能决定一篇文档是否被索引。
  6. 至于稀疏且包含了全文索引键和其他递增/递减索引键的复合索引,只有全文索引键的存在与否能决定是否索引该文档。
  7. 一个稀疏且唯一的索引,可以防止集合中的文档被索引键中出现重复值,同时也允许多个文档里不包含被索引键

(三)、 局部索引(Partial Indexes)

  1. 这是3.2.0版本以后新增的
  2. 只会对Collections满足条件partialFilterExpression的文档进行索引
  3. 使用该索引,会在一定程度上减少存储空间和创建索引和维护性能降低的成本

(四)、 TTL索引

  1. TTL 集合支持失效时间设置,当超过指定时间后,集合自动清除超时的文档,这用来保存一些诸如session会话信息的时候非常有用,或者存储缓存数据使用。**但删除会有延时 **
  2. 索引的字段必须是一个日期的 bson 类型
  3. 你不能创建 TTL 索引,如果要索引的字段已经在其他索引中使用。否则超时后文档不会被自动清除。
  4. 索引不能包含多个字段

三、 索引属性选项配置

(一)、options全局选项(适用于所有索引)

参数名 类型 描述
background Boolean 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为false
unique Boolean 建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
name string 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。不能超过128字符
partialFilterExpression Document 设定索引只对满足条件的文档起作用 具体见官网对Partial Indexes
sparse Boolean 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
expireAfterSeconds integer 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。
storageEngine Document 允许用户在创建索引时指定每一个索引的配置

(二)、 全文(文本)索引选项

参数名 类型 描述
weights document 设置文本索引字段的权重,权重值1- 99,999
default_language 设置文本分词的语言,默认为english,其他支持语言
language_override string 使用文档中的一个字段的值作为设置文本分词的语言,默认为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 标识索引升序,-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:文档扫描条目)

<font color="red">总结:在设置查询字段时应尽量只设置建立了索引的字段 </font>

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即索引在排序中没有起到作用,这说明单键索引在多个字段排序时没有作用。

<font color="red">总结:</font>

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

(二)、创建复合索引

语法: 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

<font color="red">总结:</font>

查询条件只是复合索引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中指定的所有键的排序顺序(例如递增/递减)必须和索引中的对应键的排序顺序 完全相同, 或者 完全相反

<font color="red">总结</font>

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

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

创建唯一索引

参考文章

索引策略

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容