一, 索引简介
再来老生常谈一番, 什么是索引呢? 数据库索引与书籍的索引类似. 有了索引就不需要翻整本书, 数据库可以直接在索引中查找, 在索引中找到条目以后, 就可以直接跳转到目标文档的位置, 这能使查找速度提高几个数量级.
然而, 使用索引是有代价的: 对于添加的每一个索引, 每次写操作 (插入, 更新, 删除) 都将耗费更多的时间. 这是因为, 当数据发生变动时, 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 后存在很大的差异, 这里只简单说明下, 如果想详细了解的话, 可以关注该作者 http://www.mongoing.com/eshu_explain1 的文章:
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: 文档扫描条目
来源: https://www.cnblogs.com/jmcui/p/8757299.html