
作者:赵师的工作日(赵明中)
现役Oracle ACE、MySQL 8.0 ocp、TiDB PCTA\PCTP、Elasticsearch Certified Engineer
微信公众号:赵师的工作日
CSND:赵师的工作日
MongoDB存储引擎底层也是B+TREE,故可以通过索引大幅提升查询的效率,尤其是在面对大规模数据集时,索引的使用显得尤为重要。
一、什么是 MongoDB 索引?
在数据库中,索引是一个用于加速数据检索的数据结构。它类似于一本书的目录,帮助数据库快速定位到数据的位置,避免全表扫描。在 MongoDB 中,索引是通过为文档中的字段建立数据结构来实现的,这样可以加速查询操作。
MongoDB 支持多种类型的索引,如单字段索引、复合索引、地理空间索引等。通过使用索引,MongoDB 可以大幅提升查询效率,尤其是在大数据量的情况下,查询性能的提升尤为显著。
二、MongoDB 索引的工作原理
MongoDB 使用了 B+ 树作为默认的索引结构。B+ 树是一种自平衡的树形数据结构,常用于数据库索引,它能够高效地支持范围查询、插入、删除和查找等操作。
1、B+ 树索引结构
B+ 树索引的基本结构包括:
- 节点:每个节点包含多个键(key)值对,B+ 树的叶节点保存实际的数据地址或文档 ID,非叶节点仅保存索引键和子节点的指针。
- 有序:B+ 树中的所有键值对按升序排列,这使得范围查询和排序变得非常高效。
- 平衡:B+ 树保持平衡,所有叶节点在同一层级上。这样可以确保每次查询操作的时间复杂度为 O(log n),避免了全表扫描带来的性能瓶颈。
在 MongoDB 中,当查询请求到达时,索引会根据查询条件定位到符合条件的数据位置,避免了全表扫描,显著提高了查询效率。
2、MongoDB 索引的检索流程
MongoDB 在进行查询时,首先会检查是否存在适用的索引。如果存在索引,它将按以下步骤进行索引检索:
- 解析查询条件:MongoDB 会解析查询语句,并提取出需要查找的字段。
- 选择索引:根据查询的字段和条件,MongoDB 会选择最合适的索引。MongoDB 会评估查询语句的各种条件,并选择最能提高性能的索引。
- 定位索引:通过 B+ 树索引,MongoDB 会迅速找到匹配查询条件的文档。它将遍历树结构,找到对应的叶子节点,并通过叶子节点的指针访问数据。
- 返回结果:一旦定位到匹配的文档,MongoDB 会从磁盘或内存中读取文档并返回查询结果。
如果没有合适的索引,MongoDB 将进行 全表扫描,这通常是非常低效的操作,特别是在数据量较大的时候。
三、常见的 MongoDB 索引类型
MongoDB 提供了多种类型的索引,每种索引类型针对不同的查询需求进行优化。常见的索引类型包括:
1、单字段索引
单字段索引是最基础的索引类型,它通过为某个字段创建索引加速查询操作。对于单字段查询,它能够提供显著的性能提升。例如,下面的命令为 username 字段创建一个单字段索引:
db.users.createIndex({ username: 1 });
这个索引会使得基于 username 的查询变得非常高效。
2、复合索引
复合索引是指为多个字段创建的索引,可以在查询中同时利用多个字段加速检索。复合索引可以显著优化具有多个查询条件的复杂查询。
例如,为 firstName 和 lastName 两个字段创建一个复合索引:
db.users.createIndex({ firstName: 1, lastName: 1 });
当查询使用了 firstName 和 lastName 作为查询条件时,复合索引能够提供比多个单字段索引更好的性能。
复合索引的匹配规则
- 前缀匹配:MongoDB 会根据查询条件的顺序来使用复合索引。例如,查询条件是 { firstName: “John”, lastName: “Doe” },MongoDB 会使用 firstName 和 lastName 字段的复合索引。而如果只查询 lastName,则复合索引无法被使用。
- 范围查询:如果复合索引中包含范围查询条件(如 >、<、>= 等),则复合索引只能覆盖范围查询之前的字段。例如,索引 { firstName: 1, age: -1 } 可以优化查询 { firstName: “John”, age: { $gt: 25 } },但不能优化 { age: { $gt: 25 } } 这种只使用范围条件的查询。
3、多键索引(Array Index)
MongoDB 支持为数组类型的字段创建索引。这种索引称为多键索引。多键索引会为数组中的每个元素创建一个单独的索引条目,从而提高数组字段查询的效率。
例如,假设我们有一个 tags 数组字段,并希望基于该字段查询:
db.articles.createIndex({ tags: 1 });
当我们查询包含某个特定标签的文章时,MongoDB 会使用多键索引快速定位匹配的文档。
4、地理空间索引
MongoDB 支持地理空间索引,用于加速基于地理位置的查询。MongoDB 提供了两种类型的地理空间索引:2d 和 2dsphere。
- 2d 索引:用于平面坐标系,适用于简单的二维点查询。
- 2dsphere 索引:用于球面坐标系,适用于地球表面的地理坐标查询。
例如,创建一个地理空间索引来加速基于坐标的查询:
db.places.createIndex({ location: "2dsphere" });
5、哈希索引
哈希索引用于在特定字段上的精确查找。它适用于需要快速查找单个值的场景。哈希索引不能支持范围查询,因此其使用场景较为有限。
6、唯一索引
唯一索引确保索引字段中的值是唯一的。这对于需要保证某个字段值不重复的情况(如用户名、电子邮件地址等)非常有用。
db.users.createIndex({ email: 1 }, { unique: true });
四、如何优化 MongoDB 查询性能
1、合理使用索引
- 避免过度索引:创建过多的索引会增加写入操作的开销,因为每次写入都会更新所有相关的索引。因此,只有在查询频繁使用的字段上创建索引。
- 使用复合索引:对于包含多个查询条件的复杂查询,使用复合索引可以显著提高性能。复合索引要根据查询的字段顺序进行设计。
2、监控索引使用情况
通过 explain 方法可以查看查询是否使用了索引,以及索引的选择和扫描情况:
db.users.find({ username: "john" }).explain("executionStats");
根据返回的 executionStats 信息,可以判断查询是否有效地利用了索引,并进行进一步的优化。
3、保持索引的更新
随着数据的增长,索引可能会变得不再高效。定期检查和优化索引是维持数据库性能的重要手段。例如,MongoDB 提供了
db.collection.reIndex() 方法来重新生成索引。
4、使用覆盖索引
覆盖索引指的是查询的字段都能通过索引返回,这样 MongoDB 就不需要再访问实际的数据文档,提高查询速度。创建合适的索引,可以使得 MongoDB 在处理查询时直接通过索引获取结果,而无需进行磁盘 I/O 操作。





