第五章 索引

索引可以用来优化查询,而且在某些特定类型的查询中,索引是必不可少的。
  • 什么是索引,为什么要用索引。
  • 如果选择需要建立索引的字段
  • 如何强制使用索引,如果评估索引的效率
  • 创建索引和删除索引

为集合选择合适的索引是提高性能的关键。

5.1 索引简介

数据库索引与书籍的索引类似。

不使用索引的查询成为全表扫描,也就是说,服务器必须查找完一本完整的书才能找到查询结果。

使用索引是有代价的,对于添加的每一个索引,每次写操作(插入、更新、删除)都将耗时更加多的时间。因此MongoDB限制每个集合上最多只能有64个索引。

5.1.1 复合索引简介

索引的值是按一定顺序排列的,因此使用索引键对文档进行排序非常快。然后,只要在首先使用索引键进行排序时才有用。例如,在下面排序中,username上的索引没有什么作用:

db.users.find().sort({'age':1,"username":1})

为了优化这个排序,需要在age 和username上建立索引:

db.users.ensureIndex({'age':1,"username":1})

这样就建立了一个复合索引,如果查询中多有个排序方向或者查询条件中有多个键,这个索引就非常有用。

5.1.2 使用复合索引

在多个键上建立的索引就是符合索引。复合索引比单键索引要复杂一些,但是也更强大。

1.选择键的方向

到目前为止,我们的所有索引都是升序的,但是,如果需要在两个或者多个查询条件上进行排序,可能需要要让索引键的方向不同。

为了在不同方向上优化这个复合排序,需要使用与方向相匹配的索引。

只有基于多个查询条件进行排序时,索引方向菜市比较重要的。

2.使用覆盖索引

如果你的查询只需要朝赵索引中包含的字段,那就根本没必要获取实际的文档,当一个索引包含用户请求的所有字段,可以认为这个索引副高了本次查询。在实际中,应该优先使用覆盖索引,而不是去获取实际的文档。

3.隐式索引

5.1.3 $操作符如何使用索引

有一些查询完全无法使用索引,也有一些查询能够比其他查询更搞笑的i使用索引。

1.低效率的操作符

有一些查询完全无法使用索引,比如 $where 查询和检查一个键是否存在的查询。也有其他一些操作不能高效的使用索引。

2.范围

复合索引使MongoDB能够高效地执行用友多个语句的查询。设计基于多个字段的索引时,应该将会用于精确匹配的字段放在索引的前面,将用于范围的字段放在最后。

3.or查询

通常来说,执行两次查询再将结果合并的效率不如单次查询高,因此应该尽量使用 $in 而不是使用 $or

5.1.4 索引对象和数组

MongoDB允许深入文档内部,对嵌套字段和数组建立索引。

5.1.5 索引基数

基数就是集合中某个字段用友不同的数量。

一般来说,应该在基数比较高端键上简历索引,或者至少应该吧基数较高的键放在复合索引前面。

5.2 使用explain() 和 hint()

从上面的内容可以看出,explain()能够提供大量与查询相关的信息。对于速度比较慢的查询来说,这是最重要的诊断工具之一。

最常见的explain()输出有两种类型:所用索引的查询和没有使用索引的查询。

查询优化器

MongoDB的查询优化器与其他数据库稍有不同,基本来说如果以索引能够精确匹配一个查询,那么查询优化器就会使用这个索引,不然的话,可能会有几个索引都适合你的查询。MongoDB会从这些可能的索引子集中为每次查询计划选择一个,这些查询计划是并行执行的。

这个查询计划会被缓存,这个查询接下来都会使用它。直到集合数据发生了比较大的变动。如果在最初的计划评估之后集合发生了比较大的数据变动,查询优化器就会重新挑选可行的查询计划。

5.3 何时不应该使用索引

提取较小的子数据集时,索引非常高效,也有一些查询不适用索引会更快。

表5.1 影响索引效率的属性
索引通常适用的情况 全表扫描通常适用的情况
集合较大 集合较小
文档较大 文档较小
选择性查询 非选择性查询

适用 $natural 排序有一个副作用:返回的结果是按照磁盘上的顺序排列的,对于一个活跃的集合来说,这是没有意义的:随着文档提交的增加或者缩小,文档会在磁盘上进行移动,新的文档会被写入到这些文档留下的空白位置,但是对于只需要进行插入的工作来说,如果要得到罪行的或者最早的文档,使用 $natural就非常有用了。

5.4 索引类型

创建索引时可以指定一些选项,使用不同选项简历索引会有不同的行为。

5.4.1 唯一索引

唯一索引可以确保集合的每一个文档的指定键都有唯一的值:

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

有些情况下,一个值可能无法被索引,索引储桶的大小是有限制的。如果某个索引条码超出了他的限制,那么这个条目就不会包含在索引里。

1.复合唯一索引

创建复合唯一索引时,单个键的值可以相同,但所有键的组合值必须是唯一的。

2.去除重复

在极少数情况下,可能希望直接删除重复的值。创建索引时使用dropDups选项,如果遇到重复的值,第一个会被保留,之后的重复文档都会被删除。

"dropDups"会强制性简历唯一索引,但是这个方式太粗暴了:你无法控制哪些文档被保留哪些文档被删除,对于比较重要的数据,千万不要使用"dropDups".

5.4.2 稀疏索引

唯一索引会吧null看做值,所以无法将多个缺少唯一索引中的键的文档插入到集合中。然而,在有些情况下,你可以希望唯一索引只对包含相应键的文档生效。如果有一个可能存在也可能不存在的字段,但是他存在是必须是唯一的,这时就可以将unique和sparse选项组合在一起使用。

使用sparse选项就可以创建稀疏索引。

稀疏索引不比是唯一的,只要去掉unique选项,就可以创建一个非唯一的稀疏索引。

5.5 索引管理

对于一个集合,每个索引只需要创建一次,如果重复创建相同的索引,是没有任何作用的。

所有的数据库索引信息都存储在system.indexes集合中,这是一个保留集合,不能再其中插入或者删除文档。只能通过ensureIndex或者dropIndexes对其进行操作。

5.5.1 标识索引

集合中的每一个索引都有一个名称,用于唯一标识这个索引,也可以用于服务器端来删除或者操作索引。

索引名称的长度是有限制的,所以新建复杂索引时可能需要已定义索引名称,调用getLastError就可以指定索引是否成功创建,或者失败的原因。

5.5.2 修改索引

可以使用dropIndex命令删除不在需要的索引

如果对您有用,请我喝杯咖啡吧。

赞助扫码::
../../../_images/apay.jpg ../../../_images/pay_wechat.png