唯一索引必须显式声明{unique: true},否则仅创建普通索引;若集合存在重复数据,createindex会因e11000错误失败,需先清理重复项或通过collmod的prepareunique与dryrun分步处理。

唯一索引能防止重复插入,但前提是集合里没有现成的重复数据——否则 createIndex 会直接失败,不是静默忽略。
db.collection.createIndex() 必须带 {unique: true}
唯一索引不是默认行为,必须显式声明。只写 db.users.createIndex({"email": 1}) 创建的是普通索引,不会拦住重复 email 插入。
-
db.users.createIndex({"email": 1}, {unique: true})才生效 - 复合唯一索引同理:
db.users.createIndex({"name": 1, "email": 1}, {unique: true})—— 约束的是组合值,不是单个字段 - 多键字段(如
email: ["a@b", "c@d"])也能用,MongoDB 会为数组中每个值生成索引条目,但约束仍按文档级组合判断
已有重复数据时 createIndex 会报 E11000 错误
这是最常卡住的地方:命令执行失败,错误信息里明确写出哪个键值冲突,比如:
MongoServerError: E11000 duplicate key error collection: test.users index: email_1 dup key: { email: "zhangsan@test.com" }
- 错误不是“创建失败”,而是“建索引过程被中断”,索引根本没建出来
- 不能靠加
background: true绕过——唯一性检查必须同步做,后台建索引不支持unique: true - 解决路径只有两条:删掉重复文档,或先用
distinct+aggregate找出重复项再清洗
想把已有普通索引升级为唯一索引?用 collMod + prepareUnique
直接改索引定义不行,得走两步走流程:
- 先运行
db.runCommand({collMod: "users", index: {keyPattern: {"email": 1}, prepareUnique: true}})—— 这会立刻禁止新重复插入,但不校验存量数据 - 再用
dryRun: true检查冲突:db.runCommand({collMod: "users", index: {keyPattern: {"email": 1}, unique: true}, dryRun: true}),返回的Violations数组列出所有冲突文档的_id - 手动处理完冲突后,去掉
dryRun再跑一次collMod完成转换
真正容易被忽略的点是:唯一索引对 null 和缺失字段也生效——多个文档若都没有 email 字段,它们在唯一索引下算“重复”,插入第二个时就会报错。如果业务允许空值共存,得提前用 sparse 索引替代,但 sparse 和 unique 不能同时设,必须二选一。











