单字段索引与复合索引
单字段索引
单字段索引是MongoDB中最基础的索引类型,它只针对集合中的单个字段建立索引结构。当查询条件中只包含该字段时,这种索引能显著提高查询性能。
// 创建单字段索引示例
db.users.createIndex({ username: 1 })
这个索引会按照username字段的值进行排序存储,1表示升序,-1表示降序。对于等值查询和范围查询都有效:
// 等值查询
db.users.find({ username: "john_doe" })
// 范围查询
db.users.find({ username: { $gt: "a", $lt: "m" } })
单字段索引的特点:
- 创建和维护成本低
- 适合频繁作为查询条件的字段
- 支持排序操作
- 可以显著减少集合扫描
复合索引
复合索引是建立在多个字段上的索引,字段顺序对索引效率有重大影响。MongoDB会按照索引字段的顺序存储数据。
// 创建复合索引示例
db.orders.createIndex({ customerId: 1, orderDate: -1 })
复合索引遵循"最左前缀"原则,这意味着查询条件必须包含索引最左边的字段才能使用该索引:
// 能使用索引的查询
db.orders.find({ customerId: "12345" })
db.orders.find({ customerId: "12345", orderDate: { $lt: ISODate("2023-01-01") } })
// 不能使用索引的查询
db.orders.find({ orderDate: { $lt: ISODate("2023-01-01") } })
复合索引的优势:
- 支持多字段联合查询
- 可以覆盖更多查询模式
- 能同时满足查询和排序需求
- 减少多个单字段索引的开销
索引选择策略
选择单字段还是复合索引取决于查询模式。对于简单查询,单字段索引足够;复杂查询则需要复合索引。
// 查询分析示例
db.products.find({
category: "electronics",
price: { $lt: 1000 },
rating: { $gt: 4 }
}).sort({ createdAt: -1 })
// 最佳索引可能是
db.products.createIndex({
category: 1,
price: 1,
rating: 1,
createdAt: -1
})
需要考虑的因素:
- 查询频率高的字段
- 字段的选择性(高基数字段优先)
- 排序需求
- 内存限制
- 写入性能影响
索引优化技巧
实际应用中,可以通过一些技巧优化索引性能:
- 使用覆盖查询避免回表:
// 创建覆盖索引
db.customers.createIndex({ email: 1, name: 1 })
// 查询只返回索引字段
db.customers.find({ email: "user@example.com" }, { _id: 0, email: 1, name: 1 })
- 索引交集优化:
// 两个单字段索引可能比一个复合索引更高效的情况
db.logs.createIndex({ userId: 1 })
db.logs.createIndex({ action: 1 })
// 查询可能使用索引交集
db.logs.find({ userId: "123", action: "login" })
- 部分索引减少索引大小:
// 只为活跃用户创建索引
db.users.createIndex(
{ username: 1 },
{ partialFilterExpression: { isActive: true } }
)
常见问题与解决方案
- 索引大小膨胀:
- 使用TTL索引自动清理过期数据
- 考虑使用部分索引
- 定期重建索引
- 写入性能下降:
- 评估索引的实际使用率
- 删除未使用的索引
- 在低峰期批量创建索引
- 内存不足:
- 优先保证常用查询的索引在内存中
- 使用压缩存储引擎
- 考虑分片集群分散负载
// 监控索引使用情况
db.collection.aggregate([{
$indexStats: {}
}])
实际案例分析
电商平台产品查询优化:
// 原始查询
db.products.find({
category: "phones",
brand: "Apple",
price: { $gte: 500, $lte: 1000 },
inStock: true
}).sort({ popularity: -1 }).limit(20)
// 优化后的索引
db.products.createIndex({
category: 1,
brand: 1,
price: 1,
inStock: 1,
popularity: -1
})
// 进一步优化为部分索引
db.products.createIndex({
category: 1,
brand: 1,
price: 1,
popularity: -1
}, {
partialFilterExpression: { inStock: true }
})
日志系统查询优化:
// 日志查询模式
db.logs.find({
app: "payment",
level: "error",
timestamp: { $gte: ISODate("2023-01-01"), $lte: ISODate("2023-01-02") }
}).sort({ timestamp: -1 })
// 最佳索引设计
db.logs.createIndex({
app: 1,
level: 1,
timestamp: -1
})
// 考虑分片策略
sh.shardCollection("db.logs", { app: 1, timestamp: -1 })
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:生成器与协程
下一篇:多键索引(数组字段索引)