MongoDB 索引
摘要:MongoDB索引是提升查询性能的关键机制,主要包括单字段索引、复合索引、多键索引、文本索引等类型。创建索引使用createIndex命令,遵循ESR原则设计复合索引,优先考虑覆盖索引以提高性能。日常管理可通过getIndexes()查看索引,使用explain()分析查询执行计划。最佳实践包括为频繁查询字段建索引、避免过度索引、定期检查索引大小占比等。常见问题可通过分析totalDocsE
·
MongoDB 索引概述
MongoDB 的索引是提升查询性能的核心机制。它在集合(collection)上为一个或多个字段创建有序的数据结构(默认是 B-tree),让数据库可以快速定位文档,而不必全表扫描。
1. 索引类型
| 类型 | 说明 | 典型场景 |
|---|---|---|
单字段索引 ({field: 1}) |
最常见的索引 | 按单一字段查询、排序 |
复合索引 ({a:1, b:-1}) |
覆盖多个字段,顺序重要 | 多字段过滤 + 排序 |
| 多键索引 (Multi-key) | 自动为数组字段创建 | 查询数组中包含某值的文档 |
文本索引 ({content: "text"}) |
全文搜索,支持语言停用词 | 搜索文章、评论 |
地理空间索引 (2dsphere, 2d) |
经纬度查询 | 附近地点、GeoJSON |
哈希索引 ({field: "hashed"}) |
哈希分片键 | 分片集合的均匀分布 |
TTL 索引 ({createdAt: 1}, expireAfterSeconds) |
自动删除过期文档 | 会话、日志 |
部分索引 (partialFilterExpression) |
只为满足条件的文档建索引 | 状态为 “active” 的文档 |
稀疏索引 (sparse: true) |
只为存在该字段的文档建索引 | 字段可选的情况 |
唯一索引 (unique: true) |
保证字段值唯一 | 用户名、email |
2. 创建索引的基本语法
// 单字段升序索引
db.collection.createIndex({ age: 1 })
// 复合索引(先按 status 升序,再按 createdAt 降序)
db.collection.createIndex({ status: 1, createdAt: -1 })
// 文本索引
db.articles.createIndex({ title: "text", body: "text" })
// TTL 索引(30 天后自动删除)
db.sessions.createIndex(
{ lastAccess: 1 },
{ expireAfterSeconds: 2592000 }
)
// 部分索引 + 唯一
db.users.createIndex(
{ email: 1 },
{ unique: true, partialFilterExpression: { status: "active" } }
)
提示:
createIndex是 幂等 的,重复执行只会返回已存在的索引信息。
3. 常用管理命令
| 命令 | 作用 |
|---|---|
db.collection.getIndexes() |
查看集合所有索引 |
db.collection.dropIndex("indexName") |
删除指定索引 |
db.collection.dropIndexes() |
删除除 _id 外的所有索引 |
db.collection.totalIndexSize() |
索引占用的磁盘空间 |
explain("executionStats") |
查看查询是否命中索引、扫描文档数 |
// 示例:查看执行计划
db.users.find({ age: { $gte: 30 } }).explain("executionStats")
4. 索引使用原则(最佳实践)
- 为频繁查询的字段建索引
find()、sort()、aggregate()中出现的字段。
- 遵循 ESR 原则(Equality-Sort-Range)
- 复合索引顺序:等值条件 → 排序字段 → 范围条件
// 推荐 { status: 1, createdAt: -1, score: 1 } // 查询:status="active" + 按 createdAt 降序 + score > 80 - 覆盖索引(Covered Query)
- 查询只返回索引中的字段,无需访问文档,极大提升性能。
db.users.createIndex({ email: 1, name: 1 }) db.users.find({ email: "a@x.com" }, { _id: 0, name: 1 }) // 完全覆盖 - 避免过度索引
- 每个写操作(insert/update/delete)都会更新索引,写放大。
- 定期使用
db.collection.stats()检查indexSize与size比例。
- 使用
hint()强制指定索引(调试时)db.orders.find({ customerId: 123 }).hint({ customerId: 1, orderDate: -1 })
5. 常见问题排查
| 症状 | 检查点 |
|---|---|
| 查询慢 | explain() → totalDocsExamined 是否接近集合文档数 |
| 写性能下降 | indexSize 过大、索引太多 |
| 唯一约束冲突 | 检查 unique 索引、是否有 null 或重复值 |
| 内存 OOM | 索引不适合 working set,导致频繁页面错误 |
6. 示例:完整实战案例
// 1. 创建集合
db.createCollection("orders")
// 2. 插入示例数据
db.orders.insertMany([
{ customerId: 1, status: "pending", orderDate: ISODate("2025-10-01"), total: 99 },
{ customerId: 2, status: "completed", orderDate: ISODate("2025-10-05"), total: 150 },
{ customerId: 1, status: "completed", orderDate: ISODate("2025-11-01"), total: 200 }
])
// 3. 创建复合索引(ESR 原则)
db.orders.createIndex(
{ customerId: 1, status: 1, orderDate: -1 }
)
// 4. 查询 + 排序(命中索引)
db.orders.find({
customerId: 1,
status: "completed"
}).sort({ orderDate: -1 })
// 5. 验证覆盖查询
db.orders.createIndex({ customerId: 1, total: 1 }, { name: "cust_total" })
db.orders.find(
{ customerId: 1 },
{ _id: 0, total: 1 }
).hint("cust_total") // 完全在索引中完成
7. 进一步学习资源
- 官方文档:MongoDB Indexes
- 性能调优指南:Analyze Query Performance
- Atlas 索引建议(云端自动推荐)
如需针对 特定查询、分片环境 或 Atlas 的索引优化示例,欢迎提供更多细节,我可以给出更精准的方案!
更多推荐



所有评论(0)