阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 索引的创建与优化

索引的创建与优化

作者:陈川 阅读数:12745人阅读 分类: MongoDB

索引的创建

在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的所有索引类型,每种类型适用于不同场景:

  1. 单字段索引:最简单的索引类型,适合单个字段的查询
userSchema.index({ email: 1 });
  1. 复合索引:多个字段组合的索引,查询条件包含这些字段时效率最高
userSchema.index({ lastName: 1, firstName: 1 });
  1. 多键索引:用于数组字段,为数组中的每个元素创建索引项
const blogSchema = new mongoose.Schema({
  tags: [String]
});
blogSchema.index({ tags: 1 });
  1. 文本索引:支持全文搜索
const articleSchema = new mongoose.Schema({
  title: String,
  content: String
});
articleSchema.index({ content: 'text' });
  1. 地理空间索引:用于地理位置查询
const placeSchema = new mongoose.Schema({
  location: {
    type: { type: String },
    coordinates: [Number]
  }
});
placeSchema.index({ location: '2dsphere' });

索引优化策略

正确的索引策略可以显著提升查询性能,以下是关键优化点:

  1. 选择性高的字段优先:选择区分度高的字段建立索引
// 用户名比性别更适合建索引
userSchema.index({ username: 1 });  // 高选择性
// userSchema.index({ gender: 1 });  // 低选择性
  1. 覆盖查询优化:索引包含查询所需全部字段
// 如果经常只需要username和email
userSchema.index({ username: 1, email: 1 });
  1. 查询模式匹配:索引字段顺序应与查询条件顺序一致
// 好的索引设计
userSchema.index({ status: 1, createdAt: -1 });
// 匹配查询
User.find({ status: 'active' }).sort('-createdAt');
  1. 索引交集优化:MongoDB可以使用多个索引的交集
userSchema.index({ age: 1 });
userSchema.index({ country: 1 });
// 查询会使用两个索引的交集
User.find({ age: { $gt: 18 }, country: 'CN' });

索引性能分析

了解索引使用情况对优化至关重要:

  1. 解释查询计划
const explanation = await User.find({ age: { $gt: 25 } })
  .explain('executionStats');
console.log(explanation.executionStats);
  1. 索引使用监控
// 获取集合的索引使用统计
const stats = await User.collection.stats();
console.log(stats.indexDetails);
  1. 慢查询日志分析
// 启用慢查询日志
mongoose.set('debug', function(collectionName, method, query, doc) {
  if (query.executionTime > 100) {  // 超过100ms视为慢查询
    console.log(`Slow query on ${collectionName}.${method}`, query);
  }
});

常见索引问题与解决方案

  1. 索引过多导致写入性能下降
// 每个插入/更新都需要维护所有索引
// 解决方案:定期评估并删除未使用的索引
await User.collection.dropIndex('username_1');
  1. 索引未被使用
// 可能原因:查询条件类型不匹配
// 错误示例:字符串字段使用数字查询
User.find({ phone: 123456 });  // 不会使用{phone:1}索引
// 正确做法
User.find({ phone: '123456' });
  1. 内存不足导致索引失效
// 解决方案:使用projection限制返回字段
User.find({ status: 'active' }, 'username email');
  1. 索引碎片化
// 定期重建索引
await User.collection.reIndex();

高级索引技巧

  1. 部分索引:只为满足条件的文档创建索引
userSchema.index({ username: 1 }, { 
  partialFilterExpression: { 
    status: { $eq: 'active' } 
  }
});
  1. 稀疏索引:只为存在该字段的文档创建索引
userSchema.index({ referralCode: 1 }, { sparse: true });
  1. TTL索引:自动过期文档
const sessionSchema = new mongoose.Schema({
  _id: String,
  data: Object,
  expiresAt: Date
});
sessionSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 });
  1. 隐藏索引:测试删除索引的影响
await User.collection.hideIndex('username_1');
// 测试查询性能
await User.collection.unhideIndex('username_1');

索引维护最佳实践

  1. 定期审查索引
// 获取索引使用情况
const indexStats = await User.collection.aggregate([
  { $indexStats: {} }
]).toArray();
  1. 生产环境索引变更流程
// 1. 在测试环境验证新索引效果
// 2. 低峰期创建索引(后台构建)
userSchema.index({ email: 1 }, { background: true });
// 3. 监控性能变化
  1. 索引命名规范
userSchema.index({ department: 1, role: 1 }, { name: 'dept_role_idx' });
  1. 分片集群索引策略
// 分片键必须包含在唯一索引中
userSchema.index({ tenantId: 1, email: 1 }, { unique: true });

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌