建索引时提示 duplicate key 错误,主因是集合存在重复数据;需先聚合查重、择优保留再清理,分片集群须跨分片聚合并指定分片键;索引冲突需先删旧索引;稀疏索引适用于需将 null 视为无效值的场景。

建索引时提示 duplicate key 错误怎么办
唯一索引创建失败,90% 是因为集合里已有重复数据——MongoDB 拒绝在脏数据上强加约束,这是保护而非 bug。
错误信息长这样:E11000 duplicate key error index: db.collection.$field_1 dup key: { : "xxx" }。它告诉你:字段 field 的值 "xxx" 至少出现了两次。
- 先查重复项:
db.collection.aggregate([{$group: {_id: "$field", count: {$sum: 1}}}, {$match: {count: {$gt: 1}}}], {allowDiskUse: true}) - 别直接删,先看哪些文档该保留(比如按
_id最大/最小、或createdAt最新) - 用
$group+$first/$last构造“去重后”的文档集,再deleteMany清理旧文档 - 清理完再建索引,否则会反复失败
分片集群下重复数据更难发现
分片集群中,duplicate key 错误可能根本不会报——相同值若路由到不同分片,每个分片都觉得自己没重复,插入全成功。
这种“静默重复”最危险,因为应用层查不到、日志里也没报错。排查必须跨分片聚合:
- 聚合命令必须加
{ allowDiskUse: true },否则$group内存超限直接中断 - 查询条件务必包含分片键,否则 mongos 无法把任务分发到所有分片,结果漏统计
- 示例:
db.collection.aggregate([{$match: {shard_key: {$exists: true}}}, {$group: {_id: "$field", ids: {$addToSet: "$_id"}}}, {$match: {"ids.1": {$exists: true}}}], {allowDiskUse: true})
已有非唯一索引冲突导致 createIndex 失败
想把已有的普通索引升级为唯一索引,但报 IndexOptionsConflict(错误码 85)或 IndexKeySpecsConflict(错误码 86),说明键相同但选项不兼容。
这不是数据问题,是索引元数据冲突:
- 用
db.collection.getIndexes()查出冲突索引的name - 执行
db.collection.dropIndex("conflicting_index_name")主动删掉旧索引 - 再跑
db.collection.createIndex({field: 1}, {unique: true}) - 注意:不要依赖自动生成的索引名,建时显式指定
name避免下次又撞车
稀疏索引 vs 全量唯一索引怎么选
字段允许 null 或缺失时,{unique: true} 默认放行多个 null——这常被当成“失效”,其实是设计如此。
业务需要把 null 也视为唯一占位?必须加 sparse: true:
-
{unique: true}:允许多个null,只对非空值去重 -
{unique: true, sparse: true}:跳过所有null和缺失字段,但对非空值强制唯一 - 二者不能共存;选哪个取决于你是否要把
null当作有效值参与去重
真正麻烦的是那些肉眼看不见的差异:末尾空格、零宽字符、大小写混用——这些不会触发 duplicate key 报错,但会让业务逻辑认为“重复没拦住”。查重复时得用正则或 $trim 预处理,建索引前得统一清洗。











