索引的创建与优化
索引的创建
在Mongoose中,索引是提高查询性能的关键工具。通过在模型定义时指定索引,可以显著加速数据检索速度。Mongoose支持多种索引类型,包括单字段索引、复合索引、唯一索引等。
最基本的单字段索引创建方式如下:
const userSchema = new mongoose.Schema({
username: {
type: String,
index: true // 创建单字段索引
},
email: String,
createdAt: Date
});
// 或者使用schema.index()方法
userSchema.index({ createdAt: 1 }); // 1表示升序,-1表示降序
复合索引适用于多条件查询场景:
userSchema.index({ username: 1, createdAt: -1 });
唯一索引确保字段值不重复:
const productSchema = new mongoose.Schema({
sku: {
type: String,
unique: true // 创建唯一索引
},
name: String
});
索引类型详解
Mongoose支持MongoDB的所有索引类型,每种类型适用于不同场景:
- 单字段索引:最简单的索引类型,适合单个字段的查询
userSchema.index({ email: 1 });
- 复合索引:多个字段组合的索引,查询条件包含这些字段时效率最高
userSchema.index({ lastName: 1, firstName: 1 });
- 多键索引:用于数组字段,为数组中的每个元素创建索引项
const blogSchema = new mongoose.Schema({
tags: [String]
});
blogSchema.index({ tags: 1 });
- 文本索引:支持全文搜索
const articleSchema = new mongoose.Schema({
title: String,
content: String
});
articleSchema.index({ content: 'text' });
- 地理空间索引:用于地理位置查询
const placeSchema = new mongoose.Schema({
location: {
type: { type: String },
coordinates: [Number]
}
});
placeSchema.index({ location: '2dsphere' });
索引优化策略
正确的索引策略可以显著提升查询性能,以下是关键优化点:
- 选择性高的字段优先:选择区分度高的字段建立索引
// 用户名比性别更适合建索引
userSchema.index({ username: 1 }); // 高选择性
// userSchema.index({ gender: 1 }); // 低选择性
- 覆盖查询优化:索引包含查询所需全部字段
// 如果经常只需要username和email
userSchema.index({ username: 1, email: 1 });
- 查询模式匹配:索引字段顺序应与查询条件顺序一致
// 好的索引设计
userSchema.index({ status: 1, createdAt: -1 });
// 匹配查询
User.find({ status: 'active' }).sort('-createdAt');
- 索引交集优化:MongoDB可以使用多个索引的交集
userSchema.index({ age: 1 });
userSchema.index({ country: 1 });
// 查询会使用两个索引的交集
User.find({ age: { $gt: 18 }, country: 'CN' });
索引性能分析
了解索引使用情况对优化至关重要:
- 解释查询计划:
const explanation = await User.find({ age: { $gt: 25 } })
.explain('executionStats');
console.log(explanation.executionStats);
- 索引使用监控:
// 获取集合的索引使用统计
const stats = await User.collection.stats();
console.log(stats.indexDetails);
- 慢查询日志分析:
// 启用慢查询日志
mongoose.set('debug', function(collectionName, method, query, doc) {
if (query.executionTime > 100) { // 超过100ms视为慢查询
console.log(`Slow query on ${collectionName}.${method}`, query);
}
});
常见索引问题与解决方案
- 索引过多导致写入性能下降:
// 每个插入/更新都需要维护所有索引
// 解决方案:定期评估并删除未使用的索引
await User.collection.dropIndex('username_1');
- 索引未被使用:
// 可能原因:查询条件类型不匹配
// 错误示例:字符串字段使用数字查询
User.find({ phone: 123456 }); // 不会使用{phone:1}索引
// 正确做法
User.find({ phone: '123456' });
- 内存不足导致索引失效:
// 解决方案:使用projection限制返回字段
User.find({ status: 'active' }, 'username email');
- 索引碎片化:
// 定期重建索引
await User.collection.reIndex();
高级索引技巧
- 部分索引:只为满足条件的文档创建索引
userSchema.index({ username: 1 }, {
partialFilterExpression: {
status: { $eq: 'active' }
}
});
- 稀疏索引:只为存在该字段的文档创建索引
userSchema.index({ referralCode: 1 }, { sparse: true });
- TTL索引:自动过期文档
const sessionSchema = new mongoose.Schema({
_id: String,
data: Object,
expiresAt: Date
});
sessionSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
- 隐藏索引:测试删除索引的影响
await User.collection.hideIndex('username_1');
// 测试查询性能
await User.collection.unhideIndex('username_1');
索引维护最佳实践
- 定期审查索引:
// 获取索引使用情况
const indexStats = await User.collection.aggregate([
{ $indexStats: {} }
]).toArray();
- 生产环境索引变更流程:
// 1. 在测试环境验证新索引效果
// 2. 低峰期创建索引(后台构建)
userSchema.index({ email: 1 }, { background: true });
// 3. 监控性能变化
- 索引命名规范:
userSchema.index({ department: 1, role: 1 }, { name: 'dept_role_idx' });
- 分片集群索引策略:
// 分片键必须包含在唯一索引中
userSchema.index({ tenantId: 1, email: 1 }, { unique: true });
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:静态方法与实例方法
下一篇:模型(Model)的创建与使用