E11000 duplicate key error是createIndex失败的最常见原因,源于集合中已存在重复数据,MongoDB拒绝创建唯一索引;需先用aggregate查重并清理,分片集群下还需确保索引包含分片键且不含哈希字段。

createIndex 报错 E11000 duplicate key error
这是最常见也最容易被忽略的失败原因:集合里已有重复数据,MongoDB 拒绝创建唯一索引。错误信息形如 E11000 duplicate key error collection: db.coll index: field_1 dup key: { field: "value" }。
它不是配置问题,而是数据现实冲突——MongoDB 不会帮你删数据,只报错退出。
- 先查重复项:
db.coll.aggregate([{$group: {_id: "$field", count: {$sum: 1}}}, {$match: {count: {$gt: 1}}}]) - 确认后清理:要么
deleteMany删除多余文档,要么用updateOne+$set合并业务逻辑需要保留的字段 - 注意:如果字段是嵌套路径(如
"user.email"),聚合中要用"$user.email"引用,且确保所有文档该路径存在或启用sparse: true
分片集群下 createIndex 失败:分片键不参与索引
在分片集群里,unique: true 索引必须包含分片键字段,否则直接拒绝,报错 cannot create unique index over {x: 1} with shards key pattern {shardKey: 1}。
根本原因是唯一性无法跨分片原子校验——每个 shard 只能保证自己范围内的唯一,没分片键参与,就等于放行全局冲突。
- 用
sh.status()查当前分片键,例如返回{tenantId: 1, _id: 1} - 要建唯一索引,字段必须是它的超集,比如
{tenantId: 1, email: 1}可行,{email: 1}不行 - 若业务真要单字段全局唯一(如 email),只能把该字段加进分片键,或改用
tenantId + email组合唯一
哈希分片键上设 unique: true 被禁止
只要分片键含哈希字段(如 {userId: "hashed"}),任何含该字段的索引都不能设 unique: true,错误提示明确:cannot create unique index over {userId: "hashed"} because hashed indexes do not support unique constraints。
哈希值无序、不可预测,MongoDB 无法用它做范围扫描和冲突定位,底层机制不支持。
- 哈希分片适合写入均匀分布,但天然放弃范围查询和唯一约束能力
- 若需唯一性,要么换升序分片键(
{userId: 1}),要么在应用层用 Redis Set 或分布式锁兜底 - 注意:即使索引是
{userId: "hashed", email: 1},只要userId是分片键且为哈希,整个索引仍不可设unique: true
索引创建中途失败留下半成品
大型分片集群上,网络抖动、mongos 挂掉或某个 shard 不可用,可能导致部分分片建好索引、其余未完成。此时集合处于危险状态:新写入可能被某些 shard 拒绝(因索引已生效),而其他 shard 无约束,实际数据已违反唯一性。
这不是“重试就能解决”的问题,残留元数据会持续干扰后续操作。
- 建索引前务必检查:
sh.status()和各 shard 的rs.status(),避开 primary 切换窗口 - 始终带
writeConcern: {w: "majority"}执行:db.runCommand({createIndexes: "coll", indexes: [{key: {a: 1}, unique: true}], writeConcern: {w: "majority"}}) - 失败后不要立刻重试;先查 config server:
db.getSiblingDB("config").collections.findOne({_id: "db.coll"}),看indexes字段是否残留未完成定义——手动清理风险极高,仅限 DBA 操作


















