MongoDB索引

一、MongoDB索引的分类与创建

1. 单键索引:

(1)创建普通键索引:

db.people.ensureIndex({‘username’:1})1表示升序,-1表示降序)

MongoDB有一个特殊的单键索引,那就是_id键上的索引,它是MongoDB默认自动创建的索引。

2)创建内嵌文档中键索引:

db.blog.ensureIndex({‘comments.date’:1})

MongoDB对内嵌文档中的键也可以创建索引,其方法和普通键索引的创建一样,两者还可以联合组成复合索引。

注意:对内嵌文档本身建立索引和对内嵌文档某个键建立索引是完全不一样的,两者都是单键索引,对内嵌文档进行整体完全匹配查询时使用前者,对内嵌文档某个键值进行查询时使用后者。而对数组键创建的索引则是多键索引,既可以用于完整匹配,又可以用于单个元素匹配。

2. 复合索引:

创建:db.people.ensureIndex({‘username’:1, ’age’:-1})

  这个索引表示先按username字段升序排序,username字段相同的文档按date字段降序排序。复合索引只能用于符合索引前缀的查询,例如,查询db.people.find({‘username’:’jet’, ’age’:18})和查询db.people.find({‘username’:’jet’})都能使用这个索引,而查询db.people.find({’age’:18})则无法使用这个索引。

3. 多键索引:

  当创建索引的字段为数组时,MongoDB会自动创建为多键索引。多键索引的创建方法和普通索引是一样的,只是MongoDB会对数组中的每个元素都建立一个索引条目,所以多键索引代价较高。当一个查询将某个字段值与一个数组进行精确匹配作为查询条件时,MongoDB首先使用多键索引找到所有在多键字段中包含查询数组第一个元素的所有文档,然后再在这些文档中找出与查询数组完全匹配的文档返回,没有办法使用多键索引一步到位,马上就找到与查询数组完全匹配的文档。

关于多键索引的一些限制:

(1)对于一个复合索引,每个索引最多只能包含一个值为数组类型的字段,这是为了避免索引条目爆炸性增长。

(2)不能指定一个多键索引为分片片键索引。

(3)哈希索引不能成为多键索引。

(4)多键索引不支持索引覆盖查询。

4. 哈希索引:

创建:db.blog.ensureIndex({‘title’:’hashed’})

  哈希索引是指按照某个字段的hash值来建立索引,目前主要用于MongoDB Sharded ClushterHash分片。哈希索引只能用于完全匹配查询,无法满足范围查询、模糊搜索等。

5. 全文索引:

  全文索引用于在文档中快速搜索文本。文本索引遍历集合中所有文档,分析每个文本字符串的标记和词干,找到它们的根词汇,例如'fishing'的根词汇为'fish',然后为根词汇建立索引条目。所以并不是文本中的所有字符串都会被创建全文索引,例如,你只是使用全文索引搜索“about”、"the"等这些词汇是搜索不到任何文档的。如果不使用全文索引,需要使用正则表达式进行逐个文档扫描搜索,效率低下,而且无法处理语言理解的问题。使用全文索引可以非常快地进行文本搜索,就像内置了多种语言分词机制的支持一样。创建全文索引的开销比较大,所以一般选择后台创建模式。

  MongoDB 在2.6版本以后才默认开启全文检索,如果你使用之前的版本,可以使用命令行启用全文检索:db.adminCommand({setParameter:true,textSearchEnabled:true}),或者在MongoDB配置文件中设置:

setParameter = textSearchEnabled=true

创建:

db.blog.ensureIndex({'content':'text'})

在集合所有字符串的键上创建全文索引:

db.blog.ensureIndex({'$**':'text'})

查询方法:

(1)普通查询:

db.blog.find({'$text':{'$search':'Today is Sunday'}})

(2)按文本相关度排序(相关度高的排前面):

db.blog.find({'$text':{'$search':'today is a sunday'}},{'score':{'$meta':'textScore'}}).sort({'score':{'$meta':'textScore'}})

(查询结果会多一个score字段,值越大说明相关度越高)

(3)若需查询多个关键词,关键词之间使用空格分隔,即空格表示或的关系。

db.blog.find({'$text':{'$search':'Today Sunday'}})

(每个空格分隔的字符串都被单独查询,若一个存在空格的字符串想作为一个整体被查询,则应该在两边加上引号。)

(4)若需查询不包含某个关键词,在关键词前加’-’,即’-’表示非的关系。

db.blog.find({'$text':{'$search':'Today -Sunday'}})

(先找到所有匹配的文档,再删除不匹配的文档。)

(3)若需查询同时包含多个关键词,则使用双引号将关键词括起来,表示与的关系。

db.blog.find({'$text':{'$search':'"Today" "Sunday"'}})

关于全文索引的一些限制:

(1)每次查询只能指定一个$text查询。

(2)若查询使用了$text查询则hint指令不再起作用。

(3)MongoDB全文索引还不支持中文。

(4)MongoDB全文索引忽略大小写差异。

6. 地理空间索引:

  MongoDB为坐标平面查询提供了专门的索引——地理空间索引。常用场景:找到离当前位置最经的N个场所。下面主要介绍这个类型索引的用法。

1)地理空间索引创建:

db.map.ensureIndex({‘gps’:’2d’});

地理空间索引键的值必须是某种形式的一对值,例如:

{‘gps’:[0,100]}]

{‘gps’:{‘x’:-30,’y’:30}}

默认情况下键值范围是-180~180,若想用其他值,可以选项指定最大最小值:

db.map.ensureIndex({‘gps’:’2d’},{‘min’:-1000,’max’:1000})

2)地理空间索引查询:

地理空间索引有两种查询方式:
<1> 普通查询:

db.map.find({‘gps’:{‘$near’:[40,-73]}})

按照离点(40,-73)由近及远的方式将map集合的所有文档都返回,没有limit默认返回100个文档,若不需要这么多结果可以指定limit

db.map.find({‘gps’:{‘$near’:[40,-73]}}).limit(10)

<2> 数据库命令查询:

db.runCommand({geoNear:’map’,near:[40,-73],num:10})

不同于普通查询的是,geoNear还会返回每个文档到查询点的距离,以插入数据为单位。

3)查询指定形状内的文档:

<1> 矩形:

db.map.find({‘gps’:{‘$within’:{‘$box’:[[10,20],[15,30]]}}})

(指定左下角和右上角的坐标)

<2> 圆形:

db.map.find({‘gps’:{‘$within’:{‘$center’:[[12,25],5]}}})

(指定圆心和半径)

 

二、MongoDB索引属性设置

1. 自定义索引名称:

db.blog.ensureIndex({‘comments.date’:1}, {‘name’:’date_idx’});

  若创建索引时不指定索引名称,则索引默认名称格式为:索引键名_索引方向_.....,如username_1_date_-1。索引名有字符个数限制,所以特别复杂的索引在创建时最好使用自定义名字。

2. 唯一索引:

  唯一索引可以保证索引字段不会出现相同的值。若想创建唯一索引,在第二个参数指定uniquetrue即可。默认情况下insert并不检查文档是否插入过了,所以为了避免插入的文档中包含与唯一键重复的值,可能要用安全插入才能满足要求。另外,_id键的唯一索引是不能删除的!

  如果没有对应的键,索引会将其作为null存储,所以如果对某个键建立了唯一索引但插入了多个缺少该索引键的文档,则由于文档包含null值而导致插入失败(duplicate key error)。

  在复合唯一索引中,单个键的值可以相同,只要所有键的值组合起来不同就好。

创建:

db.people.ensureIndex({‘username’:1,’date’:-1}, {‘unique’:true})

创建并消除重复:

db.people.ensureIndex({‘username’:1},{‘unique’:true,’dropDups’:true})

(加上’dropDups’参数之后,在已有集合创建索引时,会保留重复键值的第一个文档,其他有重复值的文档都删除。否则会报错:duplicate key error,并指出第一个重复的值。)

3. 过期索引:

  过期索引除了能像其他索引一样用于加速查询,还有一个特殊功能,那就是过了某个时间段之后索引会过期,MongoDB会把相应的数据删除掉。过期索引适用于会话信息、日志数据等数据集合。

创建:

db.blog.ensureIndex({'time':1}, {'expireAfterSeconds':300})(指定300秒后过期)

关于过期索引的一些限制:

(1)被索引的键必须是ISODate时间类型,否则数据不会自动删除。

(2)如果被索引的键存储了ISODate时间类型数组,则以数组中最小的时间为过期时间。

(3)过期索引不能是复合索引。

(4)无法保证索引过期之后数据会被马上删除。MongoDB删除过期数据是由后台任务每60秒运行一次来实现的,而且删除数据也需要时间,故存在延迟。

4. 稀疏索引:

  稀疏索引会跳过所有不包含索引字段的文档,只对存在索引字段的文档建立索引。

创建:

db.people.ensureIndex({'hobby':1}, {'sparse':true})

 

三、MongoDB索引管理

1. 查看某个集合所有索引信息:

db.blog.getIndexes()

2. 查看某个集合索引大小:

db.blog.totalIndexSize()

3. 让索引创建在后台完成:

db.people.ensureIndex({‘username’:1},{‘background’:true})

(‘background’参数会让索引创建过程在后台完成,同时正常处理请求(还是会有所影响的)。否则数据库会阻塞索引创建期间所有请求。)

4. 强制使用某个索引进行查询:

db.foo.find().hint({‘username’:1,’age’:1})

强制全表扫描:

db.foo.find().hint({‘$natural’:true})

5. 删除索引:

删除集合某个索引:

db.runCommand({'dropIndexes':'friend','index':'age_1'})

删除集合所有索引:

db.runCommand({'dropIndexes':'friend','index':'*'})

(删除集合也会删除所有索引,若只是删除集合所有文档则不会删除索引。)

6. 修改索引:

  MongoDB没有单独的修改索引的方法,若想修改索引只能先删除旧索引,创建新索引。

7. 查看某个查询是否使用索引:

db.foo.find().hint({‘username’:1,’age’:1}).explain()

 

四、MongoDB索引使用总结

1. 索引文件在数据库启动时导入内存中,如果索引大小超过内存限制,则无法全部导入内存,将会导致索引查询性能下降。

2. 创建索引之后,索引会占用存储空间,而且每次插入、更新和删除数据时都会产生额外的开销,因为数据库不但需要执行这些操作,还要对索引进行维护。因此要尽可能少创建索引,特别是对于插入、更新、删除等操作远远多于查询操作的集合。每个集合默认的最大索引个数为64个。

3. 只有使用复合索引前缀的查询才能使用该索引。

4. 要是查询要返回集合中一半以上的结果,用表扫描会比几乎每条文档都查索引要高效一些。

5. 使用复合索引时,MongoDB会优先选择查询条件中精确匹配的键在前面的复合索引。

6. MongoDB初次做某个查询时,查询优化器会同时尝试各种查询方案。最先完成的被确定使用,其他的则终止掉。查询方案会记录下来,以备日后应对相同键的查询。查询优化器还会定期重试其他方案,以防因为添加新的数据后,之前的方案不再是最优的了。

7. 索引的元信息存储在每个数据库的system.indexes集合中。这是一个保留集合(遍历数据库所有集合要小心),不能插入、删除文档,只能通过ensureIndexdropIndexes进行操作。System.namespaces集合也含有索引的名字。

8. 集合名长度不能超过121字节,_id索引的命名空间需要额外的6字节,所以集合名和索引名加起来不能超过127字节。一个复合索引最多可以有31个字段。

9. 为已有文档创建索引比先创建索引再插入所有文档要稍快一点。
10. MongoDB中一次查询只能使用一个索引,例如:db.blog.find({'title':'hello world','readed':'5'}),就算'title'和'readed'两个字段上都创建了索引,但是这个查询只会使用其中一个索引,使用比较高效的那一个。$or查询是个例外:db.blog.find({'$or':[{'title':'hello'},{'readed':5}]}),会使用两个索引,因为这个$or查询本来就是分两次进行查询的,最后再将结果合并返回。如果$or查询使用的是同一个字段,则最好改为$in查询,毕竟大多数情况下一次查询总要比两次查询来得快。(若$or中出现某个字段没有索引,则所有字段都不会使用索引进行查询)

11. 建立索引时要考虑的问题:

会做什么样的查询?其中哪些键需要索引?

每个键的索引方向是怎样的?

如何应对扩展?有没有一种不同的键的排列可以使常用数据更多地保留在内存中?

12. 覆盖索引查询

1)什么是覆盖索引查询?

  查询所涉及的所有字段都是索引的一部分;查询需要返回的所有字段都包含在同一个索引中。

2)覆盖索引查询的优点:

  由于所有出现在查询中的字段都是索引的一部分,MongoDB在RAM的索引上搜索到数据之后即可返回所需数据,无需再去磁盘中读取文档数据,所以要快得多。

3)不能使用覆盖索引查询的例外情况:

  索引字段是数组或子文档。

4)注意事项

  MongoDB查询中,_id键是默认返回的,如果索引中没有包含_id键,想要使用索引覆盖查询的话,应该在查询中显式设置不返回_id键,否则将不会使用索引覆盖查询。

 13. 无法使用索引的查询:

(1)正则表达式, $nin,$not,$exists 等。

(2)算术运算符,如 $mod等。

(3)$where 子句。

(4)$ne 可以使用索引,但效率低,因为需要扫描整个索引。

14. 范围列可以用到索引(必须是最左前缀),但是范围列后面的列无法用到索引。同时,索引最多用于一个范围列,因此如果查询条件中有两个范围列则无法全用到索引。

原文地址:https://www.cnblogs.com/wujuntian/p/6872823.html