外观
MongoDB 索引类型选择
基本索引类型
单字段索引
单字段索引是最基本的索引类型,基于单个字段创建索引。
特点
- 支持升序(1)和降序(-1)
- 适合基于单个字段的查询、排序和范围查询
- 创建和维护成本较低
适用场景
- 频繁基于单个字段进行查询
- 需要对单个字段进行排序
- 范围查询(如
age > 30)
创建示例
javascript
// 创建单字段索引
db.users.createIndex({ name: 1 })
// 创建降序索引
db.users.createIndex({ created_at: -1 })复合索引
复合索引基于多个字段创建索引,字段顺序对查询性能有重要影响。
特点
- 支持多个字段的组合查询
- 字段顺序遵循最左前缀原则
- 适合覆盖查询(包含查询所需的所有字段)
适用场景
- 频繁基于多个字段的组合进行查询
- 需要对多个字段进行排序
- 覆盖查询场景
创建示例
javascript
// 创建复合索引
db.orders.createIndex({ customer_id: 1, order_date: -1 })
// 最左前缀原则:该索引支持 { customer_id }, { customer_id, order_date } 查询
// 但不支持 { order_date } 查询多键索引
多键索引用于数组字段,MongoDB 会为数组中的每个元素创建索引项。
特点
- 自动为数组字段创建
- 支持基于数组元素的查询
- 支持数组字段的排序
适用场景
- 数组字段的查询(如
tags: "mongodb") - 数组字段的范围查询
- 数组字段的排序
创建示例
javascript
// 创建多键索引
db.products.createIndex({ tags: 1 })
// 查询示例
db.products.find({ tags: "database" })地理空间索引
地理空间索引用于存储和查询地理空间数据,如经纬度坐标。
类型
- 2d 索引:用于平面坐标
- 2dsphere 索引:用于地球表面的球体坐标
- geoHaystack 索引:用于高基数地理空间数据
适用场景
- 位置查询(如查找附近的商店)
- 地理围栏查询
- 距离计算
创建示例
javascript
// 创建 2dsphere 索引
db.locations.createIndex({ location: "2dsphere" })
// 查询示例:查找距离指定点 10 公里内的位置
db.locations.find({
location: {
$near: {
$geometry: {
type: "Point",
coordinates: [116.4074, 39.9042] // 北京坐标
},
$maxDistance: 10000
}
}
})文本索引
文本索引用于全文搜索,支持对字符串内容进行搜索。
特点
- 支持多种语言
- 支持短语搜索、前缀搜索
- 支持权重设置
适用场景
- 全文搜索(如博客文章内容搜索)
- 基于文本内容的过滤
- 支持多种语言的搜索
创建示例
javascript
// 创建文本索引
db.articles.createIndex({ content: "text", title: "text" })
// 设置权重
db.articles.createIndex(
{ content: "text", title: "text" },
{ weights: { title: 10, content: 1 } }
)
// 查询示例
db.articles.find({ $text: { $search: "mongodb index" } })哈希索引
哈希索引基于字段的哈希值创建索引,适合等值查询。
特点
- 只支持等值查询,不支持范围查询和排序
- 哈希值均匀分布,适合分片键
- 不支持前缀匹配
适用场景
- 等值查询(如
user_id: "12345") - 作为分片键使用
创建示例
javascript
// 创建哈希索引
db.users.createIndex({ user_id: "hashed" })
// 查询示例
db.users.find({ user_id: "12345" })稀疏索引
稀疏索引只包含有索引字段的文档,跳过没有该字段的文档。
特点
- 减少索引大小
- 提高查询性能
- 只包含有索引字段的文档
适用场景
- 索引字段不是所有文档都包含
- 减少索引存储空间
- 提高查询效率
创建示例
javascript
// 创建稀疏索引
db.users.createIndex({ email: 1 }, { sparse: true })
// 查询示例
db.users.find({ email: { $exists: true } })唯一索引
唯一索引确保索引字段的值在集合中是唯一的。
特点
- 防止重复数据
- 可以基于单个或多个字段
- 支持稀疏唯一索引
适用场景
- 确保数据唯一性(如用户邮箱)
- 防止重复插入
- 作为主键使用
创建示例
javascript
// 创建唯一索引
db.users.createIndex({ email: 1 }, { unique: true })
// 创建复合唯一索引
db.orders.createIndex({ order_no: 1, customer_id: 1 }, { unique: true })
// 创建稀疏唯一索引
db.users.createIndex({ phone: 1 }, { unique: true, sparse: true })部分索引
部分索引只包含满足指定过滤条件的文档,减少索引大小。
特点
- 基于过滤条件创建
- 减少索引大小和维护成本
- 提高查询性能
适用场景
- 只需要为部分文档创建索引
- 过滤条件明确的场景
- 减少索引存储空间
创建示例
javascript
// 创建部分索引
db.orders.createIndex(
{ total_amount: 1 },
{ partialFilterExpression: { status: "completed" } }
)
// 查询示例
db.orders.find({ status: "completed", total_amount: { $gt: 100 } })覆盖索引
覆盖索引包含查询所需的所有字段,不需要回表查询文档数据。
特点
- 提高查询性能
- 减少磁盘 I/O
- 适合频繁的查询场景
适用场景
- 频繁执行的查询
- 只需要返回少量字段的查询
- 对性能要求高的场景
创建示例
javascript
// 创建覆盖索引(包含查询所需的所有字段)
db.users.createIndex({ name: 1, email: 1, age: 1 })
// 查询示例:覆盖查询,不需要回表
db.users.find(
{ name: /^A/ },
{ name: 1, email: 1, age: 1, _id: 0 }
)索引类型选择原则
1. 基于查询模式选择
- 分析查询模式,确定最频繁的查询类型
- 优先为频繁查询的字段创建索引
- 考虑查询的选择性,选择性高的字段适合创建索引
2. 遵循最左前缀原则
- 复合索引的字段顺序对查询性能有重要影响
- 将最常用的查询字段放在最左边
- 将选择性高的字段放在最左边
3. 考虑索引大小和维护成本
- 索引会增加存储空间和写操作开销
- 平衡查询性能和索引维护成本
- 避免创建过多不必要的索引
4. 覆盖查询优先
- 优先考虑创建覆盖索引,减少回表查询
- 包含查询所需的所有字段
- 提高查询性能
5. 考虑数据分布
- 分析数据分布,选择合适的索引类型
- 对于高基数字段,考虑使用唯一索引或哈希索引
- 对于低基数字段,考虑使用部分索引
索引类型选择决策树
常见索引类型选择场景
1. 用户管理系统
场景描述
- 用户登录:基于邮箱和密码查询
- 用户列表:基于名称、创建时间排序
- 用户搜索:基于名称、邮箱的模糊搜索
索引选择
javascript
// 登录查询:唯一索引
db.users.createIndex({ email: 1 }, { unique: true })
// 用户列表:复合索引
db.users.createIndex({ created_at: -1, name: 1 })
// 用户搜索:文本索引
db.users.createIndex({ name: "text", email: "text" })2. 电商订单系统
场景描述
- 订单查询:基于用户 ID 和订单日期
- 订单状态:基于状态和创建时间
- 订单金额:基于金额范围查询
索引选择
javascript
// 订单查询:复合索引
db.orders.createIndex({ user_id: 1, order_date: -1 })
// 订单状态:部分索引
db.orders.createIndex(
{ status: 1, created_at: -1 },
{ partialFilterExpression: { status: { $in: ["pending", "processing"] } } }
)
// 订单金额:复合索引
db.orders.createIndex({ user_id: 1, total_amount: 1 })3. 内容管理系统
场景描述
- 文章查询:基于分类、发布时间
- 文章搜索:基于标题、内容的全文搜索
- 标签查询:基于标签数组
索引选择
javascript
// 文章查询:复合索引
db.articles.createIndex({ category_id: 1, published_at: -1 })
// 文章搜索:文本索引(带权重)
db.articles.createIndex(
{ title: "text", content: "text" },
{ weights: { title: 10, content: 1 } }
)
// 标签查询:多键索引
db.articles.createIndex({ tags: 1 })索引类型性能比较
| 索引类型 | 查询性能 | 写操作开销 | 存储空间 | 适用场景 |
|---|---|---|---|---|
| 单字段索引 | 高 | 低 | 小 | 单个字段的等值、范围查询 |
| 复合索引 | 高 | 中 | 中 | 多个字段的组合查询 |
| 多键索引 | 中 | 中 | 中 | 数组字段的查询 |
| 文本索引 | 中 | 高 | 大 | 全文搜索 |
| 地理空间索引 | 中 | 中 | 中 | 地理空间查询 |
| 哈希索引 | 高 | 低 | 小 | 等值查询、分片键 |
| 唯一索引 | 高 | 中 | 小 | 确保数据唯一性 |
| 稀疏索引 | 高 | 低 | 小 | 字段不是必填的场景 |
| 部分索引 | 高 | 低 | 小 | 只需要部分文档的场景 |
| 覆盖索引 | 极高 | 中 | 中 | 覆盖查询场景 |
索引类型选择最佳实践
1. 分析查询模式
- 使用
db.collection.explain()分析查询执行计划 - 使用
db.currentOp()查看当前运行的查询 - 使用
system.profile收集慢查询日志
2. 避免过度索引
- 每个集合的索引数量不宜过多(建议不超过 10 个)
- 定期清理不必要的索引
- 平衡查询性能和写操作开销
3. 监控索引使用情况
- 使用
db.collection.stats()查看索引大小和使用情况 - 使用
db.serverStatus().indexCounters查看索引计数器 - 使用
db.collection.aggregate([{ $indexStats: {} }])查看索引使用统计
4. 考虑数据增长
- 预估数据增长趋势,选择合适的索引类型
- 对于大数据集,考虑使用分片和合适的分片键
- 定期重建索引,优化索引性能
5. 测试和验证
- 在测试环境中验证索引性能
- 使用真实数据进行性能测试
- 定期回顾和优化索引策略
常见问题(FAQ)
Q1: 如何选择复合索引的字段顺序?
A1: 复合索引的字段顺序应遵循以下原则:
- 将最常用的查询字段放在最左边
- 将选择性高的字段放在最左边
- 考虑排序和范围查询的需求
- 遵循最左前缀原则
Q2: 什么时候应该使用部分索引?
A2: 当只需要为集合中的部分文档创建索引时,应使用部分索引。例如:
- 只需要为状态为 "active" 的文档创建索引
- 只需要为金额大于 100 的订单创建索引
- 只需要为特定类型的文档创建索引
Q3: 如何选择分片键的索引类型?
A3: 选择分片键的索引类型应考虑:
- 等值查询场景:使用哈希索引
- 范围查询场景:使用单字段或复合索引
- 数据分布均匀性:确保分片键的值分布均匀
- 查询模式:考虑最频繁的查询类型
Q4: 什么时候应该使用覆盖索引?
A4: 当查询只需要返回少量字段,且这些字段可以被索引覆盖时,应使用覆盖索引。例如:
- 查询用户的名称和邮箱,不返回其他字段
- 查询订单的金额和状态,不返回其他字段
- 频繁执行的统计查询
Q5: 如何处理低基数字段的索引?
A5: 对于低基数字段(如性别、状态),应考虑:
- 避免单独创建索引,除非查询频率非常高
- 作为复合索引的一部分使用
- 考虑使用部分索引,只包含常用的状态值
- 分析查询选择性,选择性低于 10% 的字段不适合创建索引
Q6: 如何选择文本索引的权重?
A6: 选择文本索引的权重应考虑:
- 将更重要的字段(如标题)设置更高的权重
- 根据业务需求调整权重值
- 测试不同权重组合的查询效果
- 定期回顾和调整权重设置
Q7: 什么时候应该使用地理空间索引?
A7: 当需要存储和查询地理空间数据时,应使用地理空间索引。例如:
- 存储和查询经纬度坐标
- 查找附近的地点
- 地理围栏查询
- 距离计算
Q8: 如何监控索引的使用情况?
A8: 监控索引使用情况的方法:
- 使用
db.collection.aggregate([{ $indexStats: {} }])查看索引使用统计 - 使用
db.serverStatus().indexCounters查看索引计数器 - 使用
db.collection.stats()查看索引大小 - 使用 MongoDB Compass 或其他监控工具查看索引性能
Q9: 如何优化多键索引的性能?
A9: 优化多键索引性能的方法:
- 限制数组元素的数量
- 避免在大型数组上创建多键索引
- 考虑使用分片,分散数据负载
- 定期重建索引,优化索引性能
Q10: 如何选择唯一索引和普通索引?
A10: 选择唯一索引和普通索引的原则:
- 当需要确保字段值的唯一性时,使用唯一索引
- 当只需要提高查询性能,不需要确保唯一性时,使用普通索引
- 唯一索引可以包含空值(最多一个文档)
- 考虑使用稀疏唯一索引,跳过没有索引字段的文档
