MongoDB 索引限制

MongoDB 索引限制(Index Restrictions)完全指南

核心结论索引不是越多越好 —— 了解 硬限制 + 性能陷阱 + 设计禁忌,才能避免 写性能崩盘、磁盘爆炸、查询失效


一、硬性限制(Hard Limits)

限制项限制值说明
单个集合索引数量64超过报错
单个索引键大小1024 bytes超过无法建索引
索引命名长度128 字符自动生成可超
索引总大小无硬限制受磁盘限制
文档大小16 MB含索引键
排序内存限制100 MB超出需磁盘排序

二、索引键大小限制(1024 bytes)

失败案例

// 字段过长 → 建索引失败
db.users.createIndex({ long_field: 1 })  // long_field = "a".repeat(2000)

错误

Index key too large: 2000 bytes > 1024 bytes

解决方案

方案说明
哈希索引createIndex({ field: "hashed" })
部分索引只索引短值
字段截断hash(field)
// 推荐:哈希索引
db.users.createIndex({ email: "hashed" })

// 或:部分索引(短 email)
db.users.createIndex(
  { email: 1 },
  { partialFilterExpression: { email: { $lte: "z" } } }
)

三、写性能限制:索引是写放大器

操作无索引1 个索引5 个索引
插入
更新索引字段10×+

实测:5 个索引 → 插入性能下降 80%

建议

  • 高并发写入:≤ 3 个索引
  • 日志系统:只建 必要索引
  • 批量导入先删索引 → 导入 → 重建
// 导入前删除
db.collection.dropIndexes()

// 导入后重建
db.collection.createIndex({ timestamp: -1 })

四、内存与磁盘限制

资源限制影响
索引驻留内存工作集 > RAM页面错误激增
索引总大小> 可用磁盘无法建新索引
排序内存100 MB超出 → 磁盘排序 → 慢

监控命令

// 索引大小
db.collection.stats().indexSize

// 页面错误
db.serverStatus().extra_info.page_faults

// 工作集
db.serverStatus().wiredTiger.cache

五、分片集群索引限制

限制说明
分片键不能是数组多键索引不可作分片键
唯一索引需含分片键否则只能在 mongos 上建
hashed 分片键不能复合只能单字段
标签索引冲突需手动管理
// 错误:数组作分片键
sh.shardCollection("mydb.logs", { tags: 1 })  // 失败

// 正确:用 _id 或 hashed
sh.shardCollection("mydb.logs", { _id: "hashed" })

六、索引类型限制

类型限制
多键索引不能与 hashed 共存
文本索引每个集合 1 个
2d 索引不支持 compound
TTL 索引只能单字段 + Date 类型
通配符索引不支持 unique
// 错误:文本索引重复
db.articles.createIndex({ title: "text" })
db.articles.createIndex({ content: "text" })  // 失败

七、查询与索引匹配限制

限制说明
前缀原则复合索引只支持前缀查询
范围中断范围查询后字段不可用
排序方向不一致无法使用索引
// 索引:{ a: 1, b: 1, c: 1 }

// 支持
{ a: 1 }
{ a: 1, b: 1 }
{ a: 1, b: { $gt: 10 } }

// 不支持(b 范围后 c 失效)
{ a: 1, b: { $gt: 10 }, c: 1 }

八、索引构建限制

限制说明
前台建索引阻塞所有写操作
后台建索引耗时长,占用 CPU
在线重建不支持修改键
// 推荐:后台建
db.collection.createIndex({ field: 1 }, { background: true })

九、索引管理限制

操作限制
dropIndex()不能删 _id 索引
collMod不能改索引键
reIndex()阻塞写

十、生产级索引限制 Checklist

检查项命令阈值
索引数量getIndexes().length≤ 10
单个索引大小stats().indexSize< 10GB
写放大压测插入< 3×
索引命中率serverStatus().indexCounters> 95%
内存驻留cache.bytes currently in the cache> 70%

十一、终极案例:避免索引爆炸

// 错误:为每个查询建索引
db.logs.createIndex({ app: 1 })
db.logs.createIndex({ level: 1 })
db.logs.createIndex({ timestamp: 1 })
db.logs.createIndex({ message: 1 })  // 64 个上限!

// 正确:复合 + 部分索引
db.logs.createIndex({
  app: 1,
  level: 1,
  timestamp: -1
}, {
  partialFilterExpression: { level: "error" }
})

// 覆盖 90% 查询

十二、索引限制黄金三问

问题检查点
1. 索引是否必要?explain() 是否 IXSCAN
2. 索引是否过大?indexSize > 10GB
3. 写性能是否下降?插入 TPS 下降 > 50%

你现在可以:

// 一行检查索引健康
db.collection.stats().indexSize
db.collection.getIndexes().length

把你的集合贴给我,我 30 秒出索引精简方案:

  1. 集合名
  2. 文档数 + 平均大小
  3. 当前索引getIndexes()

回复 3 行输出,我生成:删除冗余索引 + 合并建议 + 性能提升预估!

文章已创建 2371

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部