MongoDB 索引全攻略
任何数据库都有索引这一核心功能,索引通常能够极大的提高查询效率,如果没有索引,MongoDB 在读取数据时必须扫描集合中的每个文件,并选取那些符合查询条件的记录。这种扫描查询集合的查询方式效率非常低,特别在处理大量数据时。索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。
目录
一、索引介绍
任何数据库都有索引这一核心功能,索引通常能够极大的提高查询效率,如果没有索引,MongoDB 在读取数据时必须扫描集合中的每个文件,并选取那些符合查询条件的记录。
这种扫描查询集合的查询方式效率非常低,特别在处理大量数据时。索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构。
使用索引通常有如下的作用:
- 加速查询:通过索引,数据库能够快速定位到符合条件的文档,避免全表扫描,大大减少查询响应时间。
- 排序优化:索引可用于高效地对查询结果进行排序,无需在内存中对大量数据进行排序操作。
- 覆盖查询:当索引包含了查询所需的所有字段时,数据库可以直接从索引中获取全部数据,无需访问文档本身,减少了磁盘 I/O。
- 唯一性约束:创建唯一索引可以确保集合中指定字段的值唯一,防止插入重复数据。
在 MongoDB 中有很多种索引,下面分别看下不同的索引。
1.1 单字段索引
在单个字段上建立的索引,对于单字段索引和排序操作,索引键的排序顺序无关紧要,因为MongoDB 可以在任意方向遍历。
// users 为 collection,创建 username 索引
db.users.createIndex({username: 1})
1.2 复合索引
多个字段的自定义索引,即复合索引。复合索引中的字段顺序很重要。例如,如果复合索引由{ userid: 1, score: -1} 组成,则索引首先按userid排序,然后在每个userid值内按score排序。在查询时可以按照userid:1,score:-1或者userid:-1,score:1查询,因为这两个顺序和树的组成顺序完全相同,相反执行userid:-1,score:-1将不会使用索引。
1.3 多键索引
MongoDB 使用多键索引来索引存储在数组中的内容。如果索引一个包含数组值的字段,MongoDB 会为数组的每个元素创建单独的索引条目。这些多键索引允许查询通过匹配数组的一个或多个元素来选择包含数组的文档。如果索引字段包含数组值,MongoDB 会自动判断是否创建多键索引;你不需要明确指定多键类型。
1.4 主键索引
MongoDB 中默认为 _id 字段且不能更改,用于维护聚簇索引树。每个集合都有一个默认的索引:“_id”索引,它是一个特殊的字段,用来唯一标识集合中的每个文档。这个字段通常被用作查询文档的主键,并且MongoDB会自动为其创建一个唯一索引。
1.5 TTL 索引
类似于 Redis 的过期时间,为一个字段创建TTL索引后,超时会自动删除整个文档。TTL索引适用于那些具有有效期、需要定期清理过期数据的场景,如会话记录、日志条目、临时消息等。
db.event_log.createIndex({timestamp: 1}, {expireAfterSeconds: 604800}) // 604800秒 = 7天
1.6 地理空间索引
特别针对地理空间数据设计的索引,如2dsphere
索引用于处理经纬度坐标,支持地理位置查询(如距离计算、边界框查询等)。其他地理空间索引类型包括2d
(用于平面坐标)和geoHaystack
(已弃用)。
1.7 哈希索引
将索引字段的值通过哈希函数计算出哈希值进行索引,适用于等值查询,但不支持范围查询和排序。
1.8 创建索引时注意事项
在创建索引时不要过多添加,虽然索引可以有效的提升查询性能,但过多的索引会增加写入成本、占用更多的存储空间,并可能使查询优化器的选择变得复杂。
创建索引时可能会存在锁表,需要根据具体使用的版本而定。4.2及以上版本在创建索引过程中,仅在索引构建的开始和结束时持有排他锁,构建过程中的其余部分产生交错读取和写入操作。建议在业务低峰期添加索引。
1.9 索引效果查看
索引创建后,到底所创建索引的效果如何呢?可以通过一下方式验证,具体就是查看所以的区分度,类似于 MySQL 的 Cardinality。所得的值越接近1说明区分度越高,索引效果越好,
// 索引字段去重后的数量 / 集合的总量
db.collection.distinct("field").length/db.collection.count()
二、索引实现原理
MongoDB 是文档型数据库,使用 BSON 格式保存数据,比关系型数据库存储更方便。MySQL 是关系型数据库,数据的关联性非常强,区间访问是常见的一种场景,底层索引组织数据使用B+树,B+树由于数据全部存储在叶子节点,并且通过指针串在一起,这就很容易进行区间遍历甚至全部遍历。
MongoDB 采用 B-tree 作为其索引的主要数据结构。B-tree 是一种自平衡的多路搜索树。
自平衡:无论数据如何插入或删除,树的高度始终保持相对稳定,从而保证查询效率接近O(log N),其中N是集合中文档的数量。
多路分支:每个节点可以有多个子节点,具体数量取决于 B-tree 的阶数。高阶 B-tree 可以更有效地利用磁盘块,减少磁盘 I/O 次数。
有序存储:B-tree 节点中的键值对按照索引字段的排序规则(升序或降序)排列,便于快速定位和范围查询。
2.1 为什么使用 B-Tree
B-Tree 和 B+Tree 都是树形数据结构,用于在数据库中存储和查找数据。它们的主要区别在于数据的存储方式和查找效率。
B-Tree 的每个节点都包含键和值,所有的键值对都存储在树的内部节点和叶子节点中。这意味着一旦找到了正确的节点,就可以立即获取到对应的值,无需进一步的磁盘 I/O 操作。
而 B+Tree 只在叶子节点中存储键值对,内部节点只存储键。这意味着查找值时,即使找到了正确的内部节点,还需要进一步查找叶子节点才能获取到值,可能需要额外的磁盘 I/O 操作。
三、执行计划
MongoDB执行计划(Execution Plan)是描述 MongoDB 如何执行一条查询语句的详细过程和策略,它提供了关于查询优化器如何选择索引、如何访问数据、执行成本估计等方面的详细信息。通过分析执行计划,可以深入了解查询性能、识别潜在的优化点以及调试查询性能问题。
执行计划语句
db.collection.find({field: value}).explain('verbosity')
使用 explain()
方法附加在查询语句后面来获取执行计划。explain()
接受一个可选的verbosity
参数,用来指定返回的执行计划详细程度:
queryPlanner
(默认):提供查询计划的基本信息,包括查询类型、候选索引、选定索引及其原因、查询阶段等。executionStats
:除了查询计划基本信息外,还包括实际执行时的统计信息,如扫描的文档数、返回的文档数、执行时间、索引使用情况等。allPlansExecution
:除了上述信息外,还会实际执行所有候选索引并返回各自的执行统计,有助于对比不同索引的性能。
执行计划中需要重点关注:COLLSCAN、IXSCAN、keysExamined、docsExamined等关键字。
- COLLSCAN:代表该查询进行了全表扫描
- IXSCAN:代表进行了索引扫描
- keysExamined:代表索引扫描条目
- docsExamined:代表文档扫描条目
根据执行计划分析结果,可能要采取不同的优化策略,比如:创建或调整索引、优化查询条件、使用覆盖索引等。
综上所述,MongoDB 执行计划提供了深入理解查询执行过程和优化查询性能的关键信息。通过解读执行计划的各部分、对比不同计划、分析统计信息,可以针对性地调整索引策略、优化查询语句或调整查询选项,从而提升查询效率。结合使用相关工具和命令,可以实现对查询性能的持续监控和调优。
往期经典推荐:
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)