MongoDB中的索引(Indexes)是为了加速查询而创建的数据结构。MongoDB的索引与关系数据库的索引类似,可以显著提高读取操作的性能。
mongodb中的索引概念
- 默认索引:MongoDB在每个集合上都会自动创建一个包含
_id
字段的唯一索引。 - 自定义索引:可以根据查询需要在单个或多个字段上创建索引。
索引类型
单字段索引:在单个字段上创建索引。
db.collection.createIndex({ field: 1 }) // 1表示升序
复合索引:在多个字段上创建索引,字段的顺序很重要。
db.collection.createIndex({ field1: 1, field2: -1 }) // field1升序,field2降序
多键索引(Multikey Index):用于数组字段的索引,每个数组元素都被索引。
db.collection.createIndex({ arrayField: 1 })
文本索引(Text Index):用于文本搜索,支持对字符串内容的全文搜索。
db.collection.createIndex({ field: "text" })
哈希索引(Hashed Index):对字段值进行哈希计算,用于分片键。
db.collection.createIndex({ field: "hashed" })
地理空间索引(Geospatial Index):用于地理位置数据,支持2d和2dsphere索引。
db.collection.createIndex({ location: "2dsphere" }) // 对GeoJSON数据使用
唯一索引(Unique Index):确保索引字段的值唯一。
db.collection.createIndex({ field: 1 }, { unique: true })
部分索引(Partial Index):只索引集合中符合指定条件的文档。
db.collection.createIndex({ field: 1 }, { partialFilterExpression: { status: "active" } })
稀疏索引(Sparse Index):只索引那些包含索引字段的文档。
db.collection.createIndex({ field: 1 }, { sparse: true })
TTL索引(Time-to-Live):自动删除过期的文档,只能用于日期字段。
db.collection.createIndex( { "field": 1 }, { expireAfterSeconds: <seconds> } ) # 30天后过期 db.logs.createIndex( { "create_time": 1 }, { expireAfterSeconds: 30 * 24 * 60 * 60 } // 30天的秒数 )
索引操作
查看索引
db.collection.getIndexes()
删除索引
db.collection.dropIndex("index_name")
删除所有索引
db.collection.dropIndexes()
索引优化器
- 查询优化器(Query Optimizer):MongoDB查询优化器会根据查询条件选择最优的索引。
- Explain Plan:可以使用
explain()
方法查看查询的执行计划和使用的索引。db.collection.find({ field: value }).explain("executionStats")
查询结果和结果说明
{ "explainVersion": "1", // 显示explain命令的版本 "queryPlanner": { // 包含关于查询计划的信息 "namespace": "mydb.test1", // 查询的数据库和集合 "indexFilterSet": false, // 表示是否有索引过滤器应用, false 表示没有应用索引过滤器 "parsedQuery": { // 表示解析后的查询条件 "name": { "$eq": "name_99999" } }, "queryHash": "A2F868FD", // 查询的哈希值,用于查询计划缓存 "planCacheKey": "A3E454E0", // 查询计划缓存的键 "maxIndexedOrSolutionsReached": false, // 是否达到了最大的OR解决方案数量, false 表示未达上限 "maxIndexedAndSolutionsReached": false, // 是否达到了最大的AND解决方案数量, false 表示未达上限 "maxScansToExplodeReached": false, // 是否达到了扫描的最大数量, false 表示未达上限 "winningPlan": { // 表示被选中的查询执行计划 "stage": "FETCH", // 当前执行计划的阶段, "FETCH" 表示当前阶段是从索引中获取文档 "inputStage": { // 表示输入阶段的计划,通常是索引扫描 "stage": "IXSCAN", // 当前阶段的操作, "IXSCAN" 表示索引扫描 "keyPattern": { // 索引的键模式 "name": 1 // 表示在name字段上创建了升序索引 }, "indexName": "name_1", // 索引的名称 "isMultiKey": false, // 是否为多键索引, false 表示不是多键索引 "multiKeyPaths": { // 多键索引的路径 "name": [ ] // { "name": [ ] } 表示name字段不是多键索引 }, "isUnique": false, // 索引是否唯一, false 表示索引不是唯一的 "isSparse": false, // 索引是否稀疏, false 表示索引不是稀疏的 "isPartial": false, // 索引是否部分索引, false 表示索引不是部分索引 "indexVersion": NumberInt("2"), // 索引版本, 2 表示索引的版本号 "direction": "forward", // 索引扫描的方向, "forward" 表示升序扫描 "indexBounds": { // 索引范围 "name": [ "[\"name_99999\", \"name_99999\"]" // 表示查询范围为"name_99999"到"name_99999" ] } } }, "rejectedPlans": [ ] // 被拒绝的计划列表, [] 表示没有被拒绝的计划 }, "executionStats": { // 包含关于查询执行的统计信息 "executionSuccess": true, //****** 查询是否成功执行, true 表示成功 "nReturned": NumberInt("1"), //****** 返回的文档数量 "executionTimeMillis": NumberInt("0"), //****** 查询执行使用了多长时间(毫秒) "totalKeysExamined": NumberInt("1"), // 扫描的索引键总数, 1 表示扫描了1个索引键 "totalDocsExamined": NumberInt("1"), //****** 扫描的文档总数, 1 表示扫描了1个文档 "executionStages": { // 执行阶段的详细统计信息 "stage": "FETCH", // 执行阶段, "FETCH" 表示从索引中提取文档 "nReturned": NumberInt("1"), // 从此阶段返回的文档数量, 1 表示返回了1个文档 "executionTimeMillisEstimate": NumberInt("0"), // 执行时间的估算值(毫秒) "works": NumberInt("2"), // 当前阶段的工作次数, 2 表示工作了2次 "advanced": NumberInt("1"), // 当前阶段的“高级”次数, 1 表示有1次“高级”操作 "needTime": NumberInt("0"), // 当前阶段的“需要时间”次数, 0 表示没有“需要时间”操作 "needYield": NumberInt("0"), // 当前阶段的“需要让步”次数, 0 表示没有“需要让步”操作 "saveState": NumberInt("0"), // 当前阶段的“保存状态”次数, 0 表示没有“保存状态”操作 "restoreState": NumberInt("0"), // 当前阶段的“恢复状态”次数, 0 表示没有“恢复状态”操作 "isEOF": NumberInt("1"), // 是否到达文件末尾, 1 表示到达文件末尾 "docsExamined": NumberInt("1"), // 在此阶段检查的文档数量, 1 表示检查了1个文档 "alreadyHasObj": NumberInt("0"), // 已经拥有对象的次数, 0 表示没有“已经拥有对象”的操作 "inputStage": { // 输入阶段的详细统计信息(通常是IXSCAN) "stage": "IXSCAN", // 输入阶段的操作, "IXSCAN" 表示索引扫描 "nReturned": NumberInt("1"), // 从此阶段返回的文档数量, 1 表示返回了1个文档 "executionTimeMillisEstimate": NumberInt("0"), // 执行时间的估算值(毫秒) "works": NumberInt("2"), // 输入阶段的工作次数, 2 表示工作了2次 "advanced": NumberInt("1"), // 输入阶段的“高级”次数, 1 表示有1次“高级”操作 "needTime": NumberInt("0"), // 输入阶段的“需要时间”次数, 0 表示没有“需要时间”操作 "needYield": NumberInt("0"), // 输入阶段的“需要让步”次数, 0 表示没有“需要让步”操作 "saveState": NumberInt("0"), // 输入阶段的“保存状态”次数, 0 表示没有“保存状态”操作 "restoreState": NumberInt("0"), // 输入阶段的“恢复状态”次数, 0 表示没有“恢复状态”操作 "isEOF": NumberInt("1"), // 是否到达文件末尾, 1 表示到达文件末尾 "keyPattern": { // 索引的键模式 "name": 1 // 表示在name字段上创建了升序索引 }, "indexName": "name_1", // 索引的名称 "isMultiKey": false, // 是否为多键索引, false 表示不是多键索引 "multiKeyPaths": { // 多键索引的路径 "name": [ ] }, "isUnique": false, // 索引是否唯一, false 表示索引不是唯一的 "isSparse": false, // 索引是否稀疏, false 表示索引不是稀疏的 "isPartial": false, // 索引是否部分索引, false 表示索引不是部分索引 "indexVersion": NumberInt("2"), // 索引版本, 2 表示索引的版本号 "direction": "forward", // 索引扫描的方向, "forward" 表示升序扫描 "indexBounds": { // 索引范围 "name": [ "[\"name_99999\", \"name_99999\"]" ] }, "keysExamined": NumberInt("1"), // 扫描的索引键数量, 1 表示扫描了1个索引键 "seeks": NumberInt("1"), // 索引扫描的次数, 1 表示进行了1次索引扫描 "dupsTested": NumberInt("0"), // 测试的重复记录数, 0 表示没有测试重复记录 "dupsDropped": NumberInt("0") // 丢弃的重复记录数, 0 表示没有丢弃重复记录 } } }, "command": { // 表示执行的实际命令 "find": "test1", // 查询的集合 "filter": { // 查询条件 "name": "name_99999" }, "$db": "mydb" // 数据库名称 }, "serverInfo": { // 提供关于MongoDB服务器的信息 "host": "71.xx", // 服务器的主机名 "port": NumberInt("27017"), // 服务器的端口 "version": "7.0.12", // MongoDB的版本 "gitVersion": "b6513ce0781db6818e24619e8a461eae90bc94fc" // MongoDB的Git版本 }, "serverParameters": { // 显示MongoDB服务器的内部参数, 这些参数通常用于调试和性能调优 "internalQueryFacetBufferSizeBytes": NumberInt("104857600"), "internalQueryFacetMaxOutputDocSizeBytes": NumberInt("104857600"), "internalLookupStageIntermediateDocumentMaxSizeBytes": NumberInt("104857600"), "internalDocumentSourceGroupMaxMemoryBytes": NumberInt("104857600"), "internalQueryMaxBlockingSortMemoryUsageBytes": NumberInt("104857600"), "internalQueryProhibitBlockingMergeOnMongoS": NumberInt("0"), "internalQueryMaxAddToSetBytes": NumberInt("104857600"), "internalDocumentSourceSetWindowFieldsMaxMemoryBytes": NumberInt("104857600"), "internalQueryFrameworkControl": "trySbeRestricted" }, "ok": 1 //****** 表示命令是否成功执行, 1 表示成功 }
stage的选项
1. COLLSCAN(Collection Scan)
- 说明: 执行全表扫描,逐一检查每个文档,以满足查询条件。
- 适用场景: 当没有合适的索引时,MongoDB会执行全表扫描。
2. IXSCAN(Index Scan)
- 说明: 执行索引扫描,使用索引来查找匹配的文档。
- 适用场景: 当查询可以利用索引加速时,MongoDB会使用索引扫描。
3. FETCH(Fetch)
- 说明: 从索引中获取文档,并检索实际的文档数据。
- 适用场景: 通常在IXSCAN之后执行,以从索引中提取实际文档。
4. SORT(Sort)
- 说明: 对文档进行排序。
- 适用场景: 当查询结果需要排序时使用。
5. LIMIT(Limit)
- 说明: 限制返回的文档数量。
- 适用场景: 当查询包含limit操作符时使用。
6. PROJECT(Project)
- 说明: 选择返回的字段。
- 适用场景: 当查询使用projection操作符来指定返回的字段时使用。
7. REDUCE(Reduce)
- 说明: 对查询结果进行汇总或合并。
- 适用场景: 通常在某些复杂的聚合操作中出现。
8. MERGE(Merge)
- 说明: 将多个结果合并为一个结果集。
- 适用场景: 通常在复合查询或聚合管道中出现。
9. UNWIND(Unwind)
- 说明: 将数组字段展开为多个文档。
- 适用场景: 在处理数组字段时使用,特别是在$unwind阶段的聚合管道中。
10. GROUP(Group)
- 说明: 将文档分组并计算汇总值。
- 适用场景: 在聚合管道中的$group阶段出现。
11. LOOKUP(Lookup)
- 说明: 执行集合间的联合操作。
- 适用场景: 在聚合管道中的$lookup阶段出现。
12. OUT(Out)
- 说明: 将聚合结果输出到另一个集合。
- 适用场景: 在聚合管道中的$out阶段出现。
13. INDEXONLY(Index Only)
- 说明: 查询只使用索引,不需要访问实际的文档。
- 适用场景: 当查询只需要索引中的字段时使用。
14. SHARD(Shard)
- 说明: 执行跨分片的查询操作。
- 适用场景: 在分片集群中,当查询需要跨多个分片时使用。
15. REPARTITION(Repartition)
- 说明: 对数据进行重新分区。
- 适用场景: 在某些复杂的聚合管道中可能会出现。
16. PLATFORM(Platform)
- 说明: 表示使用了某种平台或引擎的特殊执行阶段。
- 适用场景: 与MongoDB的特定引擎或平台相关的操作。
索引的局限性
- 写操作开销:索引会增加写操作的开销,因为每次写操作都需要更新索引。
- 存储空间:索引会占用额外的存储空间,特别是多键索引可能会大幅增加存储需求。