一、索引简介

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

然而,使用索引是有代价的:对于添加的每一个索引,每次写操作(插入、更新、删除)都将耗费更多的时间。这是因为,当数据发生变动时,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. postgresql和oracle数据库对比

    SQL执行计划干预 从使用postgresql来看,想要改变执行计划只能通过対表进行分析,不能通过添加hint的方式来改变执行计划: oracle不仅可以通过对表进行收集统计来改变执行计划,而且很重要 ...

  2. linux下安装软件

    业界的软件标准安装有三步:configure,make,make install,下面是它们的定义: ./configure是用来检测你的安装平台的目标特征的.比如它会检测你是不是有CC或GCC,并不 ...

  3. 提高UI设计效率的4个技巧

    提高UI设计效率的4个技巧 如何提高UI设计的速度?在这里分享一些我观察到的常见问题和改善方式.当然,需要注意的地方何其多. 身为设计师的你,应该要了解工程实作的基本原理: 业界 NG 率:接近 10 ...

  4. numpy用法小结

    前言 个人感觉网上对numpy的总结感觉不够详尽细致,在这里我对numpy做个相对细致的小结吧,在数据分析与人工智能方面会有所涉及到的东西在这里都说说吧,也是对自己学习的一种小结! numpy用法的介 ...

  5. 剑指Offer-平衡二叉树

    package Tree; /** * 平衡二叉树 * 输入一棵二叉树,判断该二叉树是否是平衡二叉树. * 平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法), ...

  6. eclipse报错排解

    一.解决eclipse中git插件中的cannot open git-upload-pack问题 有时候在eclipse上使用插件egit向github或者osc上同步代码时,有时候会发现出现cann ...

  7. 研华ADAM 6000系列型号枚举值

    public enum Adam6000Type    {        Non = 0,        Adam6015 = 6015,        Adam6017 = 6017,        ...

  8. oracle 常用sql字符函数介绍

    常用字符函数介绍 1.ascii 返回与指定的字符对应的十进制数: SQL>select ascii('A') A,ascii('a') a,ascii('0') zero,ascii(' ') ...

  9. Semaphore 源码分析

    Semaphore 源码分析 1. 在阅读源码时做了大量的注释,并且做了一些测试分析源码内的执行流程,由于博客篇幅有限,并且代码阅读起来没有 IDE 方便,所以在 github 上提供JDK1.8 的 ...

  10. SIMD---AVX系列

    AVX全称Advanced Vcetor Extension,是对SSE的后续扩展,主要分为AVX.AVX2.AVX512三种.在目前常见的机器上,大多只支持到AVX系列,因此其他SIMD扩展指令我们 ...