一、索引简介

再来老生常谈一番,什么是索引呢?数据库索引与书籍的索引类似。有了索引就不需要翻整本书,数据库可以直接在索引中查找,在索引中找到条目以后,就可以直接跳转到目标文档的位置,这能使查找速度提高几个数量级。

然而,使用索引是有代价的:对于添加的每一个索引,每次写操作(插入、更新、删除)都将耗费更多的时间。这是因为,当数据发生变动时,MongoDB不仅要更新文档,还要更新集合上的所有索引。因此,MongoDB限制每个集合上最多只能有64个索引。通常,在一个特定的集合上,不应该拥有两个以上的索引。于是,挑选合适的字段建立索引非常重要。

  • 索引基数

基数(cardinality)就是集合中某个字段拥有不同值的数量。比如 gender 字段,基数一般就男女 2个而已;而像 mobile 这样的字段,基数就会特别大。

通常来讲,一个字段的基数越高,这个字段上的索引就越有用。这是因为索引能够迅速将搜索范围缩小到一个比较小的结果集。对于低基数的字段,索引通常无法排除掉大量可能的匹配。假设我们在"gender"上有一个索引,需要查找名为Susan的女性用户。通过这个索引,只能将搜索空间缩小到大约50%。

tips:在关系型数据库中类似 gender 这样的字段可以使用位图索引。

  • 索引原理浅析

我们以一个索引 {"age" : 1, "username" : 1} 来看看索引在MongoDB 中是如何存储的,大致是这个样子:

每一个索引条目都包含一个"age"字段 和 "username"字段,并且指向文档在磁盘中的存储位置。注意,这里的 age 严格的按照升序排序,并且相同的 age 对应的 username 也严格的按照升序排序。

来看个例子 :db.users.find({"age" : 21}).sort({"username" : -1})

这个索引对于这个查询来说是非常高效的,因为它可以马上定位到 age = 21 的位置,并且age = 21 中的 username 已经是排序好的。

tips:排序方向并不重要:MongoDB可以在任意方向上对索引进行遍历。
tips:查询中的字段顺序无关紧要,MongoDB 会自动找出可以使用索引的字段,而无视查询的字段顺序。

  • $操作符如何使用索引

有一些查询完全无法使用索引,也有一些查询能够比其他查询更高效地使用索引。

$where:无法使用索引。
$nin:无法使用索引。
$exists:无法使用索引。因为在索引中,不存在的字段和null字段的存储方式是一样的,查询必须遍历每一个文档检查这个值是否真的为null还是根本不存在。
$ne:可以使用索引,但并不是很高效。因为必须遍历整个索引条目才能找到结果的文档。
$not:能够使用索引,但通常不知道如何使用索引,从而退化成全表扫描。
$or:能够使用索引,但是$or 查询会将 or 的条件拆分成多个独立的查询,然后再将结果合并在一起。这是很低效的,不建议用。建议用 $in 取代 $or 。

设计多键索引的时候要记得,要把基数大的字段放在索引的前面,因为这样能更快缩小查询的范围。

二、索引类型

  • 复合(组合)索引

复合索引就是一个建立在多个字段上的索引。
如果查询中有多个排序方向或者查询条件中有多个键,复合索引就非常有效。

db.userInfo.ensureIndex({"age":1,"age":1})

进行多键排序时,索引的方向尤为重要。尽量做到多键排序的方向和复合索引的方向是一致的,因为这能很大的避免在内存中进行排序的运算。
tips:相互反转(在每个方向上都乘以-1)的索引是等价的:{"age" : 1, "user name" : -1}适用的查询与{"age" : -1, "username" : 1}是完全一样的。

复合索引具有双重功能,而且对不同的查询可以表现为不同的索引。如果有一个{"age" :1, "username" : 1}索引,"age"字段会被自动排序,就好像有一个{"age" : 1}索引一样。因此,这个复合索引可以当作{"age" : 1}索引一样使用。

  • 唯一索引

唯一索引可以确保集合的每一个文档的指定键都有唯一值。我们熟悉的 "_id" 索引就是一个唯一索引(但它不能被删除,而其他唯一索引是可以删除的)。

db.users.ensureIndex({"username" : 1}, {"unique" : true})

定义了唯一索引后,这个键就不允许插入重复的值了,否则会抛异常。
tips:A 字段不存在 和 A 字段为 null 是互斥的!

在已有的集合上创建唯一索引可能会报错,因为集合中可能已经有重复的值了。在极少数情况下,可能希望直接删除重复的值。创建索引时使用"dropDups"选项,如果遇到重复的值,第一个会被保留,之后的重复文档都会被删除。

db.users.ensureIndex({"username" : 1}, {"unique" : true, "dropDups" : true})

  • 稀疏索引

在有些情况下,你可能希望唯一索引只对包含相应键的文档生效。如果有一个可能存在也可能不存在的字段,但是当它存在时,它必须是唯一的,这时就可以将unique和sparse选项组合在一起使用,创建唯一稀疏索引。注意:MongoDB中的稀疏索引(sparse index)与关系型数据库中的稀疏索引是完全不同的概念。基本上来说,MongoDB中的稀疏索引只是不需要将每个文档都作为索引条目。并且,稀疏索引并不一定是唯一的。

db.ensureIndex({"email" : 1}, {"unique" : true, "sparse" : true})

当某个查询使用了稀疏索引,就不会返回不包含这个字段的文档。因为稀疏索引并没有把每个文档都作为索引条目。

  • 覆盖索引

如果你的查询只需要查找索引中包含的字段,那就根本没必要获取实际的文档。当一个索引包含用户请求的所有字段,可以认为这个索引覆盖了本次查询。所以,尽可能使用投射筛选返回的字段,比如 {"_id":0,"age":1} 等,来实现覆盖索引。

三、索引管理

  • 新建索引

普通索引

db.userInfo.ensureIndex({"name":1},{"name","MyIndex"})
"1" 表示按照name进行升序,"-1" 表示按照name进行降序。
默认的索引以 key1_1_key2_-1 这样的方式命名,可以手动指定索引的名字,如上。

对象索引

可以对整个对象建立索引,或者对对象的某个元素使用索引。

db.users.ensureIndex({"loc" : 1})
只有在进行与对象字段顺序完全匹配的子文档查询时(比如db.users.find({"loc" :{"ip" : "123.456.789.000", "city" : "Shelbyville", "state" :"NY"}}})),查询优化器才会使用"loc"上的索引。

db.users.ensureIndex({"loc.city" : 1})
有涉及到对象city的查询都会使用这个索引。

数组索引

对数组建立索引,实际上是对数组的每个元素建立一个索引条目。比如一个文档中的数组字段有20个元素,那么该文档就拥有了20个索引条目!所以对数组字段的索引建立要慎重。

  • 删除索引

db.userInfo.dropIndexes("name_1")
删除指定索引

db.userInfo.dropIndexes()
删除除了_id 以外的所有索引

  • 操作索引

获取当前索引列表:db.userInfo.getIndexes()

hint 暴力选择某种索引:db.userInfo.find({name:'zhangsan',birthday:'1989-3-2'}).hint({"name":1,"birthday":1})

强制使用全表扫描:db.userInfo.find({"birthday" : {"$lt" :"1989-3-2"}}).hint({"$natural" : 1})

索引分析函数explain:MongoDB 3.0前 和 MongoDB 3.0后存在很大的差异,这里只简单说明下,如果想详细了解的话,可以关注该作者的文章:

MongoDB 3.0 前:db.driverLocation.find({"areaCode":"350203"}).explain()

cursor:表扫描方式 (basicCursor:顺序查找)
nscanned:浏览了多少文档
n:最终返回了几个文档
millis:总共耗时了多少毫秒
scanAndOrder:是否必须在内存中对数据进行排序

MongoDB 3.0 后:db.driverLocation.find({"areaCode":"350203"}).explain("executionStats")

executionTimeMillis:该query的整体查询时间
nReturned:查询返回的条目
totalKeysExamined:索引扫描条目
totalDocsExamined:文档扫描条目

MongoDB系列四(索引).的更多相关文章

  1. MongoDB系列一(索引及C#如何操作MongoDB)

    索引总概况 db.test.ensureIndex({"username":1})//创建索引 db.test.ensureIndex({"username": ...

  2. 4.MongoDB系列之索引(一)

    1. 执行计划查看 db.getCollection('users').find({'username': 'shenjian'}).explain('executionStats') 结果查看,先大 ...

  3. MongoDB系列四:解决secondary的读操作

    http://blog.itpub.net/26812308/viewspace-2124660/ 在Replica sets 中的secondary节点默认是不可读的.使用Replica Sets实 ...

  4. 5.MongoDB系列之索引(二)

    1. $运算符如何使用索引 1.1 低效的运算符 $ne.$not查询可以使用索引,但不是很有效,尽量避免 1.2 范围查询 范围查询其实是多值查询,根据复核索引规则,尽可能先等值精确匹配,然后范围查 ...

  5. 【转载】8天学通MongoDB——第四天 索引操作

    这些天项目改版,时间比较紧,博客也就没跟得上,还望大家见谅. 好,今天分享下mongodb中关于索引的基本操作,我们日常做开发都避免不了要对程序进行性能优化,而程序的操作无非就是CURD,通常我们 又 ...

  6. 8天学通MongoDB——第四天 索引操作

    这些天项目改版,时间比较紧,博客也就没跟得上,还望大家见谅. 好,今天分享下mongodb中关于索引的基本操作,我们日常做开发都避免不了要对程序进行性能优化,而程序的操作无非就是CURD,通常我们 又 ...

  7. Node.js学习系列总索引

    Node.js学习系列也积累了一些了,建个总索引方便相互交流学习,后面会持续更新^_^! 尽量写些和实战相关的,不讲太多大道理... Node.js学习笔记系列总索引 Nodejs学习笔记(一)--- ...

  8. MongoDB学习笔记(索引)

    一.索引基础:    MongoDB的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的优化技巧.下面是创建索引的命令:    > db.test.ensureIndex({" ...

  9. MongoDB学习笔记(索引)(转)

    一.索引基础:    MongoDB的索引几乎与传统的关系型数据库一模一样,这其中也包括一些基本的优化技巧.下面是创建索引的命令:    > db.test.ensureIndex({" ...

随机推荐

  1. 学习ASP.NET Core Razor 编程系列二——添加一个实体

    在Razor页面应用程序中添加一个实体 在本篇文章中,学习添加用于管理数据库中的书籍的实体类.通过实体框架(EF Core)使用这些类来处理数据库.EF Core是一个对象关系映射(ORM)框架,它简 ...

  2. 轮评审用例,写用例的重要性-----(python单元测试反思)

    时间过的真快,3月底了,更新一次博客吧,算是对三月份忙碌的一个总结. 吃过饭,习惯登录qq,看到我群里的一个大神,碎冰发的一个作业 不就是写个代码吗,然后写完再进行测试这个代码是否实现了这个功能. 于 ...

  3. Oracle 12c(12.1.0.5)OEM server agent 安装配置

    注意: 此文档为生产上操作文档,省略了IP,oracle用户server,agent 端至少需要sudo,ping,ssh,创建目录权限. 一.安装要求 1.1. 系统情况一览 IP 数据库 OEM ...

  4. spring-boot-devtools

    Create a new Maven Project  and  we have two class under the package com.example.demo like below scr ...

  5. Linux下面如何用tcpdump抓包

    很多时候我们的系统部署在Linux系统上面,在一些情况下定位问题就需要查看各个系统之间发送数据报文是否正常,下面我就简单讲解一下如何使用tcpdump抓包 tcpdump是Linux下面的一个开源的抓 ...

  6. oracle中事务处理--事务隔离级别

    概念:隔离级别定义了事务与事务之间的隔离程度. ANSI/ISO SQL92标准定义了一些数据库操作的隔离级别(这是国际标准化组织定义的一个标准而以,不同的数据库在实现时有所不同). 隔离级别 脏读 ...

  7. supervisor进程管理工具的使用

    supervisor是一款进程管理工具,当想让应用随着开机启动,或者在应用崩溃之后自启动的时候,supervisor就派上了用场. 广泛应用于服务器中,用于引导控制程序的启动 安装好superviso ...

  8. 删除日志释放空间最好不要用rm

    目前在维护一些服务器有一个根目录空间经常告警no space left ,切到/var/log 目录下du -sh * 的时候,发现有一个authlog占了12G,然后立马执行了rm authlog: ...

  9. 新手使用mac上的textedit写HTML时遇到的问题及解决办法

    刚开始在mac上学习HTML,总结一下遇到的问题和解决办法 问题:使用textedit编写html,在网页上却仍然显示的是代码. 解决办法: 打开textedit后打开文本编辑 选择偏好设置 按如图所 ...

  10. 北京工业大学耿丹学院2016下C作业学习总结

    北京工业大学耿丹学院2016下C的班级地址在https://edu.cnblogs.com/campus/bjgygd/Sixteen-One . 第一次作业:两部分 第一部分:新建博客,书写第一篇随 ...