唯一索引可确保索引字段不存储重复值,即强制索引字段的唯一性。默认情况下,MongoDB在创建集合期间会在_id字段上创建唯一索引。
新的内部格式
从MongoDB 4.2开始,对于4.2(或更高版本)的featureCompatibilityVersion(fCV),MongoDB使用一种新的内部格式来存储与早期MongoDB版本不兼容的唯一索引。新格式适用于现有的唯一索引以及新创建/重建的唯一索引。
一、创建唯一索引
创建唯一索引,要使用db.collection.createIndex()方法,并且将unique选项设置为true。
db.collection.createIndex( <key and index type specification>, { unique: true } )
1.单字段的唯一索引
例如,要在members集合的user_id字段上创建唯一索引,在mongo shell中使用以下操作:
db.members.createIndex( { "user_id": 1 }, { unique: true } )
2.唯一复合索引
还可以对复合索引实施唯一约束。要对复合索引使用唯一约束,则MongoDB将对索引键值的组合实施唯一性。
例如,要在members集合的groupNumber,lastname和firstname字段上创建唯一索引,请在mongo shell中使用以下操作:
db.members.createIndex( { groupNumber: 1, lastname: 1, firstname: 1 }, { unique: true } )
创建的索引对groupNumber,lastname和firstname值的组合实施唯一性。
对于另一个示例,请考虑包含以下文档的集合:
{ _id: 1, a: [ { loc: "A", qty: 5 }, { qty: 10 } ] }
在a.loc和a.qty上创建唯一的复合多键索引:
db.collection.createIndex( { "a.loc": 1, "a.qty": 1 }, { unique: true } )
唯一索引允许将以下文档插入到集合中,因为该索引对于a.loc和a.qty值的组合具有唯一性:
db.collection.insert( { _id: 2, a: [ { loc: "A" }, { qty: 5 } ] } )
db.collection.insert( { _id: 3, a: [ { loc: "A", qty: 10 } ] } )
二、行为
1.限制条件
如果集合已经包含违反索引唯一约束的数据,则MongoDB无法在指定的索引字段上创建唯一索引。
2.在副本集和分片集群上构建唯一索引
对于副本集和分片集群,使用滚动过程创建唯一索引要求在该过程中停止对集合的所有写操作。如果在此过程中无法停止对集合的所有写操作,请不要使用滚动过程。而是通过以下方式在集合上构建唯一索引:
- 在主数据库(primary)上为副本集执行db.collection.createIndex(),或
- 在mongos上为分片群集执行db.collection.createIndex()。
3.跨单独文档的唯一约束
唯一约束适用于集合中的单独文档( separate documents)。即,唯一索引可防止单独的文档具有相同的索引键值。
因为该约束适用于单独的文档,所以对于唯一的多键索引,文档可能具有导致重复索引键值的数组元素,只要该文档的索引键值不重复另一个文档的索引键值即可。在这种情况下,重复的索引条目仅插入索引一次。
例如,考虑具有以下文档的集合:
{ _id: 1, a: [ { loc: "A", qty: 5 }, { qty: 10 } ] }
{ _id: 2, a: [ { loc: "A" }, { qty: 5 } ] }
{ _id: 3, a: [ { loc: "A", qty: 10 } ] }
在a.loc和a.qty上创建唯一复合多键索引:
db.collection.createIndex( { "a.loc": 1, "a.qty": 1 }, { unique: true } )
如果集合中没有其他文档的索引关键字值为{ "a.loc": "B", "a.qty": null },则唯一索引允许将以下文档插入到集合中。
db.collection.insert( { _id: 4, a: [ { loc: "B" }, { loc: "B" } ] } )
4.唯一索引和缺失字段
如果文档在唯一索引中没有索引字段的值,则索引将为此文档存储一个空值。由于唯一约束,MongoDB将只允许一个缺少索引字段的文档。如果存在多个没有索引字段值的文档,或者缺少索引字段,则索引构建将失败,并给出重复键错误。
例如,一个集合在x上具有唯一索引:
db.collection.createIndex( { "x": 1 }, { unique: true } )
如果集合尚不包含缺少字段x的文档,则唯一索引允许插入不包含字段x的文档:
db.collection.insert( { y: 1 } )
但是,如果集合中已经包含缺少字段x的文档,则再次插入没有字段x的文档时,则出现唯一索引错误:
db.collection.insert({z:1})
上述操作由于违反了对字段x值的唯一约束,因此无法插入文档,错误信息如下:
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "E11000 duplicate key error index: test.collection.$a.b_1 dup key: { : null }"
}
})
5.唯一部分索引
3.2版中的新功能。
部分索引仅对集合中符合指定过滤器表达式的文档建立索引。如果同时指定partialFilterExpression和唯一约束,则唯一约束仅适用于满足过滤器表达式的文档。
如果文档不满足过滤条件,则具有唯一约束的部分索引不会阻止插入不符合唯一约束的文档。有关示例,请参见具有唯一约束的部分索引。
6.分片集群和唯一索引
对于远程分片集合,只有以下索引可以是唯一的:
- 分片键上的索引
- 复合索引,其中分片键是前缀
- 默认的_id索引;但是,如果_id字段不是分片键或分片键的前缀,则_id索引仅对每个分片强制执行唯一性约束。
唯一性和_ID索引
如果_id字段不是分片键或分片键的前缀,则_id索引仅对每个分片(而非跨分片)强制实施唯一性约束。
例如,考虑一个跨越两个分片A和B的分片集合(具有分片键{x:1})。由于_id键不是分片键的一部分,因此该集合可能在分片A中具有_id值为1的文档。以及分片B中_id值为1的另一个文档。
如果_id字段不是分片键,也不是分片键的前缀,则MongoDB期望应用程序在整个分片上强制_id值的唯一性。
唯一索引约束意味着:
- 对于要分片的集合,如果该集合具有其他唯一索引,则无法分片该集合。
- 对于已分片的集合,不能在其他字段上创建唯一索引。