MongoDB 索引限制
MongoDB 索引提高了查询性能,但也有一些限制和注意事项,在设计数据库时需要了解这些限制,以避免潜在的性能问题和错误。
1. 每个集合的最大索引数量
- MongoDB 限制:每个集合最多可以创建 64 个索引(包括
_id索引)。 - 影响:
- 索引过多会增加存储开销,影响写入性能(因为每次插入、更新都需要更新索引)。
- 应该合理规划索引,避免创建不必要的索引。
2. 索引字段大小限制
- MongoDB 限制:
- 单个索引项的大小(索引键+索引元数据)不能超过 1024 字节。
- 适用于 BSON 字段值、字符串、数组等。
- 影响:
- 超长字符串索引可能会失败:
js db.articles.createIndex({ title: 1 })
如果title太长,索引可能无法创建。 - 解决方案:
- 使用哈希索引:
js db.articles.createIndex({ title: "hashed" }) - 截取字段值存储短哈希。
- 使用哈希索引:
3. 复合索引字段顺序
- MongoDB 限制:
- 复合索引字段顺序很重要,查询时必须匹配索引的前缀字段才能利用索引。
- 影响:
- 索引
{ A: 1, B: 1 }:- ✅
db.collection.find({ A: 10 })能使用索引。 - ✅
db.collection.find({ A: 10, B: 20 })能使用索引。 - ❌
db.collection.find({ B: 20 })不能使用索引。
- ✅
- 解决方案:
- 根据查询习惯优化索引顺序,最常用的字段放前面。
4. 多键索引(Multikey Index)限制
多键索引用于数组字段索引,但有一些限制:
- 不能用于:
- 复合索引的多个字段都包含数组(MongoDB 无法索引多个数组组合)。
- 覆盖查询(因为索引存储方式不同)。
- 示例(错误案例):
db.products.createIndex({ tags: 1, categories: 1 }) // ❌ tags 和 categories 都是数组
- 正确方式:
- 仅索引一个数组字段:
js db.products.createIndex({ tags: 1 }) // ✅ - 分开查询,不要依赖复合索引。
5. 唯一索引与 null 值
- MongoDB 限制:
- 唯一索引(
unique: true)不会阻止多个文档的null值,MongoDB 认为null值不算重复。 - 示例:
db.users.createIndex({ email: 1 }, { unique: true })
- 允许多个
email: null进入集合,因为null在唯一索引中不算冲突。 - 解决方案:
- 使用 部分索引 限制
null值:js db.users.createIndex( { email: 1 }, { unique: true, partialFilterExpression: { email: { $exists: true } } } ) - 这样,只有
email存在时才会被索引,避免多个null存在。
6. TTL 索引的限制
TTL(Time-To-Live)索引用于自动删除过期文档,但有以下限制:
- 只能用于
Date类型字段,不能用于Number或String类型。 - TTL 索引的时间间隔不是精确的,MongoDB 约60 秒扫描一次并删除过期文档。
- 无法手动删除 TTL 索引管理的文档,只能等 MongoDB 处理。
- 示例:
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })
- 但
createdAt必须是 Date 类型,否则 MongoDB 不会自动删除。
7. 哈希索引限制
哈希索引用于等值查询,但不能用于范围查询($gt、$lt)。
- 示例(错误案例):
db.users.createIndex({ userId: "hashed" })
db.users.find({ userId: { $gt: "abc" } }) // ❌ 不能使用索引
- 正确方式:
- 如果需要范围查询,应该使用普通索引:
js db.users.createIndex({ userId: 1 }) // ✅ 可用于范围查询
8. 覆盖索引(Covered Query)限制
MongoDB 只有在查询的字段都在索引内,且不查询 _id 时,才能进行覆盖索引查询。
- 示例(正确方式):
db.users.createIndex({ name: 1, age: 1 })
db.users.find({ name: "Alice" }, { name: 1, age: 1, _id: 0 }) // ✅ 覆盖索引
- 示例(错误方式):
db.users.find({ name: "Alice" }, { name: 1, age: 1 }) // ❌ 由于 _id 默认返回,无法覆盖索引
- 解决方案:
- 显式排除
_id,确保查询的字段都在索引内。
9. 大量写入时索引影响性能
索引会影响写入(插入、更新、删除)性能,因为每次写入时,MongoDB 都需要同步更新索引。
- 建议:
- 批量插入前,可以先删除索引,然后再重新创建:
js db.collection.dropIndexes() // 删除所有索引 // 进行批量插入 db.collection.createIndex({ field: 1 }) // 重新创建索引 - 避免在高写入负载下创建多个索引,尽量优化索引结构。
10. Oplog(复制集)和索引限制
MongoDB 复制集(Replica Set) 依赖 oplog,但如果索引过大,会影响同步性能:
- 问题:
- 索引过大会占用 oplog 空间,导致主从复制性能下降。
- 优化:
- 减少不必要的索引。
- 优化查询模式,减少索引负担。
总结
| 限制类型 | 影响 | 解决方案 |
|---|---|---|
| 索引数量限制 | 每个集合最多 64 个索引 | 只创建必要索引 |
| 索引大小限制 | 单个索引项 ≤1024 字节 | 使用哈希索引或短字段 |
| 复合索引顺序 | 必须匹配前缀顺序 | 选择合理索引顺序 |
| 多键索引限制 | 不能索引多个数组字段 | 仅索引单个数组 |
唯一索引 null 值 | null 不受唯一约束影响 | 使用部分索引 |
| TTL 索引限制 | 仅支持 Date 类型 | 确保字段类型正确 |
| 哈希索引限制 | 不能做范围查询 | 用普通索引 |
| 覆盖索引限制 | 必须查询所有索引字段 | 显式排除 _id |
| 高写入影响索引 | 写入性能下降 | 先删除索引再批量插入 |
更多详细内容请关注其他相关文章!