MongoDB:索引
介绍
索引是一种用来快速查询数据的数据结构。B+Tree 就是一种常用的数据库索引数据结构,MongoDB 采用 B+Tree 做索引 ,索引创建在colletions上。
MongoDB主要使用B+树作为其索引结构。与普通的B树相比,B+树在叶子节点上存储了所有的数据记录(或指向记录的指针),而非仅存储键值对。这样做的好处是可以减少IO操作,提高数据的读取效率。同时,B+树的叶子节点之间会互相链接,形成链表结构,这有助于范围查询等操作的优化。
MongoDB不使用索引的查询,先扫描所有的文档,再匹配符合条件的文档。
使用索引的查询,通过索引找到文档,使用索引能够极大的提升查询效率。
MongoDB中的索引通常存储在磁盘上,以文件的形式存储在数据库目录中。索引文件包含了索引的结构信息以及索引所对应的文档数据的引用。
索引的作用
提高查询性能:索引可以显著减少数据库引擎需要扫描的数据量,从而加快查询速度。
加速数据检索:对于大型数据集,索引可以大大缩短数据检索的时间。
支持排序和范围查询:索引可以优化排序和范围查询操作。
支持唯一性约束:通过创建唯一索引,可以确保数据库表中每一行数据的唯一性。
索引类型
-
单字段索引(Single Field Indexes):这是最基本的索引类型,它可以基于集合中的单个字段创建。
示例:
db.collection.createIndex({field:1})
,其中1
表示升序索引,-1
表示降序索引。 -
复合索引(Compound Indexes):复合索引是单字段索引的升级版,它可以基于集合中的多个字段创建。
示例:
db.collection.createIndex({field1:1,field2:-1})
,表示在field1
上建立升序索引,在field2
上建立降序索引。 -
唯一索引(Unique Indexes):唯一索引确保索引字段的值在集合内是唯一的。
示例:
db.collection.createIndex({field:1},{unique:true})
,在field
上创建唯一索引。 -
多键索引(Multikey Indexes):多键索引针对数组或者嵌套文档的字段进行索引。
当索引的字段是数组时,MongoDB会为数组中的每个元素创建索引项。
-
地理空间索引(Geospatial Indexes):地理空间索引对包含地理坐标的字段进行索引,支持对地理空间数据的查询。
MongoDB提供了两种类型的地理空间索引:2dsphere和2d。
-
文本索引(Text Indexes):文本索引可用于字符串字段,主要用于执行全文搜索。
示例:
db.collection.createIndex({field:"text"})
,在field
上创建文本索引。 -
哈希索引(Hashed Indexes):哈希索引在某些特定的应用场景下可以提供比普通索引更好的性能,但它不支持范围查询。
示例:
db.collection.createIndex({field:"hashed"})
,在field
上创建哈希索引。 -
通配符索引(Wildcard Indexes):通配符索引使用通配符对任意字段进行索引,提供了一种灵活的方式来索引集合中的未知或动态变化的字段。
注意:这种索引类型可能在某些MongoDB版本中不是默认支持的,或者需要特定的配置才能使用。
-
TTL索引(Time-To-Live Indexes):TTL索引是一种特殊的索引,用于自动删除集合中过期的数据。
示例:
db.collection.createIndex({expireField:1},{expireAfterSeconds:3600})
,在expireField
上创建TTL索引,设置数据过期时间为3600秒。 -
部分索引(Partial Indexes):部分索引只针对满足特定条件的文档进行索引,这有助于减少索引的大小并提高查询性能。
示例(假设创建):
db.collection.createIndex({field:1},{partialFilterExpression:{condition:true}})
,其中condition
定义了哪些文档应该被索引。
索引覆盖
索引覆盖查询是指查询操作可以仅通过索引来获取所需的数据,而无需访问集合中的文档。换句话说,当查询的字段都包含在索引中时,MongoDB可以直接使用索引来执行查询,而无需进一步加载文档数据。
为了实现索引覆盖,查询字段必须全部包含在索引中。
示例
假设在MongoDB中有一个集合,包含以下文档:
|
|
可以针对查询字段name
和age
创建覆盖索引:
|
|
接下来,执行一个查询,查找年龄小于等于30岁的文档,并只返回name
和age
字段:
|
|
在执行上述查询时,MongoDB将使用覆盖索引来执行查询,直接从索引中获取name
和age
字段的值,并返回给用户,无需访问文档数据,从而提高了查询性能。
创建索引
在MongoDB中,创建索引可以显著提升数据查询的性能,尤其是对于大型数据集。MongoDB提供了多种类型的索引,包括单一字段索引、复合索引、唯一索引、地理空间索引、全文搜索索引等。
在MongoDB中,你可以通过createIndex()
函数来创建索引。
从MongoDB 3.0开始,推荐使用
createIndex()
,而之前的版本使用的是ensureIndex()
。
基本语法:
|
|
keys
参数定义了索引的类型和方向。例如,{ field: 1 }
创建一个升序索引,{ field: -1 }
创建一个降序索引。options
参数是一个可选的对象,用于设置索引的额外选项,例如是否唯一、是否稀疏、是否是文本索引等。
常见示例
单一字段索引
|
|
指定索引名称
name
参数允许你指定索引的名称。如果不指定,MongoDB将自动生成一个名称。在创建复合索引或具有特殊选项(如唯一索引)的索引时,指定名称很有用。
|
|
复合索引
|
|
唯一索引
|
|
稀疏索引(仅对非空字段创建索引)
|
|
部分索引
部分索引仅索引满足指定过滤条件的文档。
在MongoDB 3.2及更高版本中,可以使用
partialFilterExpression
来创建部分索引。
|
|
过期索引(TTL,Time-To-Live)
通过expireAfterSeconds
参数,可以创建TTL索引,这些索引会根据文档中的某个时间戳字段自动删除旧文档。
|
|
文档级别的唯一性(_id字段自动创建)
不需要显式创建。
地理空间索引
|
|
全文搜索索引
|
|
哈希索引
|
|
查看索引信息
可以使用getIndexes()
方法来查看集合中的索引信息。
|
|
删除索引
在 MongoDB 中,dropIndex()
方法用于删除一个指定的索引。
在较新的 MongoDB 版本和官方 shell 中,你通常应该传递索引名称(字符串)给 dropIndex()
方法,而不是索引的键模式(对象)。例如,如果你要删除一个基于 age
字段的索引,你首先需要知道该索引的名称,然后使用该名称来删除索引:
|
|
这里的 "age_1"
是索引的名称,它是由 MongoDB 自动生成的,其中 _1
表示升序索引。
在旧版本的 MongoDB 或者某些驱动程序中,确实可以使用键模式对象来删除索引,但是这种方式并不推荐,因为它可能导致意外删除同名的多个索引。例如,在某些旧版本的驱动程序中,下面的代码可能可以工作:
1
db.stus.dropIndex({age:1});
但是,这并不是最佳实践,而且在较新版本的 MongoDB 中可能无法正常工作。最好的做法是获取索引的完整名称,然后使用名称来删除索引。
为了安全地删除索引,你应该先使用 getIndexes()
或 listIndexes()
方法来查看所有索引的名称,然后使用正确的索引名称调用 dropIndex()
方法。
查看查询是否经过了索引
在MongoDB中,要查看查询是否经过了索引,你可以使用explain()
方法来分析查询的执行计划。explain()
方法会返回一个包含查询执行计划的文档,其中包含了索引使用情况等信息。
对于基本的查询,你可以通过链式调用find()
和explain()
方法来实现,如下所示:
|
|
执行上述命令后,MongoDB会返回查询的执行计划文档。
执行计划文档包含了多个字段,其中与索引使用相关的关键字段包括:
-
queryPlanner.winningPlan:这表示查询过程中选择的最佳执行计划。你可以在这个计划中查看索引的使用情况。
-
stage:表示查询优化器为给定查询选择的最优执行计划的当前阶段。如果
stage
值为COLLSCAN
,则表示全集合扫描;如果值为FETCH
,则说明经过了索引。queryPlanner.winningPlan.stage字段的详细说明`queryPlanner.winningPlan.stage`在MongoDB的`explain()`方法的输出中是一个非常重要的字段,它表示查询优化器为给定查询选择的最优执行计划的当前阶段。这个字段提供了关于查询如何被执行的关键信息,包括查询使用的操作类型(如索引扫描、集合扫描、文档检索等)。 具体来说,`queryPlanner.winningPlan.stage`可以包含以下几种类型的阶段(stage),每种类型都代表了查询执行的不同方面: 1. COLLSCAN:集合扫描。这表示MongoDB正在对集合进行全表扫描来查找匹配的文档。这通常发生在没有可用的索引或者查询条件无法有效利用索引时。 2. IXSCAN:索引扫描。这表示MongoDB正在使用索引来查找匹配的文档。索引扫描可以显著提高查询性能,因为它减少了需要检查的文档数量。 3. FETCH:检出文档。这个阶段通常与索引扫描(IXSCAN)一起使用,表示MongoDB已经根据索引找到了文档的位置,现在正在从集合中检索这些文档。 4. SHARD_MERGE:合并分片中结果。在分片集合中,这个阶段表示MongoDB正在合并来自不同分片的查询结果。 5. SORT:排序。这表示MongoDB正在对查询结果进行排序。如果查询中包含排序操作(如`sort()`),并且没有使用索引进行排序(即索引顺序与排序顺序不匹配),则可能会出现这个阶段。 6. LIMIT:限制返回数。这表示查询结果的数量被限制了(如使用`limit()`)。 7. SKIP:跳过。这表示查询结果中的一部分文档被跳过了(如使用`skip()`)。 8. IDHACK:针对_id进行查询。这是一个特殊的阶段,表示查询直接通过_id字段来检索文档,这通常非常快,因为_id字段总是被索引的。 9. SHARDING_FILTER:分片中过滤掉孤立文档。在分片集合中,这个阶段用于过滤掉不属于查询请求的分片中的文档。 10. COUNT、COUNTSCAN、COUNT_SCAN:这些阶段与计数操作相关,表示MongoDB正在计算查询结果的数量。 11. SUBPLA:未使用到索引的*or*查询的*stage*返回。这表示查询中使用了‘or`操作符,但MongoDB没有找到可以使用的索引来优化查询。 12. TEXT:使用全文索引进行查询时的阶段。 13. PROJECTION:限定返回字段时的阶段。这表示查询结果中的文档只包含了指定的字段。 `queryPlanner.winningPlan.stage`字段的具体值取决于查询的复杂性、集合的结构、索引的可用性等多种因素。通过分析这个字段,你可以了解MongoDB是如何执行你的查询的,并据此优化查询性能。例如,如果你发现查询正在执行全表扫描(COLLSCAN),而你预期它应该使用索引(IXSCAN),那么你可能需要检查你的索引是否正确创建,或者查询条件是否可以有效利用索引。
-
inputStage:这表示执行计划的输入阶段。如果
inputStage
中包含了IXSCAN
(索引扫描),则说明查询经过了索引。
-
-
executionStats.totalDocsExamined:这个字段表示查询过程中扫描的文档数。如果这个数字远小于集合中的文档总数,且查询条件中包含了索引字段,那么很可能查询经过了索引。如果这个数字接近或等于集合中的文档总数,那么可能查询没有有效地使用索引。
当你在 MongoDB 中使用 explain()
方法时,默认情况下它会返回一个简化版的执行计划,称为“queryPlanner”模式,该模式不包含实际的执行统计信息。
为了获取详细的执行统计信息,包括 totalDocsExamined
这样的指标,你需要以特定的模式调用 explain()
方法。你应该使用 "executionStats"
模式来获得更完整的执行信息,这将包括实际运行查询时的统计信息。
这里是使用 "executionStats"
模式的示例:
|
|
这将返回一个包含 executionStats
字段的输出,该字段将提供查询的详细执行信息,包括:
executionSuccess
: 是否成功执行了查询。nReturned
: 返回的文档数量。executionTimeMillis
: 查询的执行时间(毫秒)。totalKeysExamined
: 被检查的索引键总数。totalDocsExamined
: 被检查的文档总数。executionStages
: 查询执行的各个阶段的详细信息。
确保在生产环境中谨慎使用 "executionStats"
模式,因为它实际上会执行查询并收集统计信息,这可能会对性能产生轻微的影响,尤其是在处理大量数据时。在开发和测试环境中使用此模式来调试和优化查询是最佳实践。
此外,如果你使用的是 MongoDB 的可视化工具如 MongoDB Compass,它也提供了类似的 explain()
功能,并且可以选择不同的模式来查看执行计划和统计信息。