在分片集合上创建索引必须包含 shard key 前缀或完全匹配,否则报错;全局唯一索引需同时满足 unique: true、包含完整 shard key 且建议 background: true;索引名需准确指定才能删除。

分片集合上创建索引必须先有 shard key
没建好 shard key 就直接 createIndex,MongoDB 会报错 Cannot create index on a sharded collection without specifying the shard key。这不是权限或版本问题,而是架构强制约束:所有索引必须包含 shard key 的前缀(或完全匹配),否则路由层无法定位数据。
比如集合按 {region: 1, userId: 1} 分片,那合法索引只能是:
-
{region: 1, userId: 1, createdAt: -1}(shard key 前缀) -
{region: 1, status: 1}(shard key 第一个字段开头) -
{region: 1, userId: 1}(完全匹配 shard key)
而 {createdAt: -1} 或 {userId: 1, region: 1}(顺序错)都不被允许。
使用 ensureIndex 或 createIndex 时必须显式指定 shard key 字段
即使你只是想加一个单字段索引,也得把 shard key 字段带上——不能省略,也不能靠后台自动补全。
错误写法:db.users.createIndex({email: 1})
正确写法(假设 shard key 是 {orgId: 1}):db.users.createIndex({orgId: 1, email: 1})
注意两点:
- 如果已有同名索引但字段顺序/方向不同,MongoDB 不会覆盖,需先
dropIndex - 对大集合建索引建议加
{background: true},否则会阻塞写入 - 不要在
mongos上用db.runCommand({createIndexes: ...})绕过校验——它仍会拒绝非法索引定义
唯一索引需要额外加 fullDocument: true 参数才能跨分片生效
普通唯一索引在分片环境下只保证单个 shard 内唯一,不是全局唯一。要真正实现全局唯一,必须用 unique: true + collation: {locale: "simple"}(可选)+ background: true,且关键是要让 MongoDB 知道你要走全局唯一路径。
正确方式是:在 createIndex 中显式传入 {unique: true, background: true},并确保该索引包含完整 shard key —— MongoDB 自动识别这是全局唯一约束,底层会启用两阶段提交协调各 shard。
常见误判点:
- 只加
unique: true但没包含完整 shard key → 创建失败 - 用了
{partialFilterExpression: {...}}的唯一索引 → 不支持全局唯一,会被拒绝 - 在已有数据的集合上建全局唯一索引 → 如果存在重复值,命令直接失败,不会跳过
删除索引后,新索引重建可能触发 chunk 迁移
删掉一个被频繁查询的索引后立刻建新索引,尤其当新索引字段参与 query routing(比如含 shard key)时,mongos 可能重新评估 chunk 分布策略,触发 chunk 拆分或迁移。这不是 bug,是平衡读写负载的正常反应。
生产环境建议:
- 避开业务高峰执行索引变更
- 用
sh.status()观察 chunk 分布是否稳定后再上线新查询 - 如果发现迁移卡住,检查 config server 是否响应正常、网络延迟是否突增
最易忽略的是:索引名默认由字段名和方向生成,但如果你手动指定 name,后续删索引必须用那个 name,而不是字段组合——名字错了就删不掉,还会干扰新索引创建。


















