Published on

Mongodb Index | 索引 相关优化 及 Mongoose 注意事项

Authors
  • avatar
    Name
    Shelton Ma
    Twitter

1. Mongodb 索引

在 MongoDB 中,是否使用索引取决于多个因素,包括索引的类型、查询方式以及数据分布情况.

1. 否使用索引的影响因素

  1. 索引选择性:如果 查询 的值非常分散(高基数),索引会很有效.如果只有几个可能的值(低基数,例如 1-5),MongoDB 可能会直接进行 全表扫描(Collection Scan).

  2. 数据规模:对于小数据集,即使有索引,MongoDB 可能会直接扫描整个集合,而不会使用索引.

  3. 索引是否覆盖查询:

    • 如果查询只涉及索引字段,MongoDB 可以 直接从索引中返回结果(覆盖索引查询,Index-Only Query).
    • 如果查询还需要其他字段,MongoDB 可能需要进行 回表查询(Fetching Documents),影响查询性能.

2. 如何确认 MongoDB 是否使用了索引

  1. 使用 explain 确认查询

    db.collection.find({status: "deleted"}).explain()
    // 如果 IXSCAN(Index Scan) 出现在 stage 字段中,说明使用了索引.
    // 如果 COLLSCAN(Collection Scan) 出现,说明 MongoDB 没有使用索引,可能是因为索引不合适或数据量较小.
    db.collection.find({status: "deleted"}).explain("executionStats")
    

3. 复合索引的影响

在 MongoDB 中,针对多个查询条件,单独索引(单列索引) 和 联合索引(复合索引) 各有适用场景.

  1. 使用单列索引, 适用:

    • 字段单独使用频率高
    • 字段没有明确的查询组合模式
    • 需要多个索引以供不同查询优化: 单列索引可以在查询优化时由 MongoDB 进行 索引交叉(Index Intersection),但效果不如复合索引.
    • ❌ 使用组合索引查询时会有回表操作
    • ❌ 占用空间
  2. 组合索引:

    • 查询经常包含多个字段
    • 排序优化(如果查询中 field1 过滤,field2 进行 sort,复合索引可以避免 MongoDB 进行额外的排序操作)
    • 覆盖索引: 如果查询 find({ field1: 10, field2: 20 }, { field1: 1, field2: 1, _id: 0 }),复合索引可以直接从索引返回数据,而不需要回表读取文档.
  3. 📌 **索引的“最左前缀”匹配:**索引的字段顺序很重要: createIndex({ field1: 1, field2: 1 })

    • 适用于 find({ field1: 10 })
    • 适用于 find({ field1: 10, field2: 20 })
    • 不适用于 find({ field2: 20 })(因为 field1 不在查询中,索引不会被高效利用)

4. 最佳实践

  1. 如果查询经常包含 field1 和 field2,使用复合索引

  2. 如果查询需要排序 (sort({ field2: 1 })),使用复合索引可直接用索引排序,而不是在内存中排序.

  3. 如果使用索引排序, 索引和排序规则要一致, 比如:

    db.collection.createIndex({ age: 1, score: -1 }); 
    db.find({ age: 20 }).sort({ score: -1 })
    
  4. 如果 field1 和 field2 组合查询少,但 field2 需要高效查询,可以同时使用单独索引和复合索引

  5. 查询条件顺序不影响索引的使用, 查询引擎会自动优化并按照索引的顺序执行

  6. 当查询条件无法确定时, 建立单列索引方便mongodb使用交叉索引, 交叉索引只能用在$and查询, $or查询无效

  7. 其他处理管理

    1. 定期检查索引,避免无用索引影响性能.
    2. 使用 createIndexes() 以手动控制索引创建
    3. 同步索引 await User.syncIndexes();

2. Mongoose 更新索引

1. 创建索引

userSchema.index({ age: 1 }); // 添加索引

2. Mongoose 添加索引后,已存在的 Collection 会更新吗?

  1. 如果你在已存在的 Collection 中添加索引,Mongoose 会尝试创建索引,但不会自动删除或修改已存在的索引

  2. 手动触发索引同步

    await User.syncIndexes(); // 删除旧索引,并同步新的索引
    
  3. 生产环境需要:

    1. 定期检查索引,避免无用索引影响性能.
    2. 使用 createIndexes() 以手动控制索引创建
    3. 同步索引 await User.syncIndexes();

3. 聚合优化

aggregate 操作可以通过限制返回字段内容来优化性能.实际上,限制返回字段是提升 MongoDB 聚合性能的一个有效手段.具体来说,减少不必要的数据传输和处理,尤其是在聚合管道的早期阶段,能显著提高性能.