唯一索引
唯一索引确保索引字段不存储重复值。单个字段的唯一索引确保给定字段中的值最多只出现一次。唯一组合索引确保索引键值的任何给定组合最多只出现一次。默认情况下,MongoDB在创建集合时在_id字段上创建唯一索引。
您可以在UI中创建和管理唯一索引(对于托管在MongoDB Atlas中的部署。
创建唯一索引
要创建唯一索引,请使用db.collection.createIndex()
方法并将unique
选项设置为true
。
db.collection.createIndex( <key and index type specification>, { unique: true } )
单一字段上的唯一索引
例如,要在 members
集合的 user_id
字段上创建唯一索引,请在 mongosh
:
db.members.createIndex( { "user_id": 1 }, { unique: true } )
唯一组合索引
您还可以在 组合索引 上强制实施唯一约束。唯一 组合索引 在索引键值的 组合 上强制实施唯一性。
例如,要在 members
集合的 groupNumber
、lastname
和 firstname
字段上创建唯一索引,请在 mongosh
:
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.insertMany( [ { _id: 2, a: [ { loc: "A" }, { qty: 5 } ] }, { _id: 3, a: [ { loc: "A", qty: 10 } ] } ] )
行为
限制
MongoDB无法在指定的索引字段上创建唯一索引,如果集合已经包含违反唯一约束的数据。
您不能在哈希索引上指定唯一约束。
在副本集和分片集群上构建唯一索引
对于副本集和分片集群,使用滚动过程创建唯一索引需要在过程中停止集合的所有写入。如果在过程中无法停止集合的所有写入,则不要使用滚动过程。相反,为了在集合上构建您的唯一索引,您必须
在副本集的主副本上运行
db.collection.createIndex()
在分片集群的
mongos
上运行db.collection.createIndex()
跨文档的唯一约束
唯一约束适用于集合中的不同文档。也就是说,唯一索引防止不同文档具有相同索引键的值。
由于约束适用于不同文档,对于唯一的多键索引,一个文档可以具有数组元素,这些元素会产生重复的索引键值,只要该文档的索引键值不重复其他文档的索引键值。在这种情况下,重复的索引条目只会插入索引一次。
例如,考虑一个包含以下文档的集合
{ _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.insertOne( { _id: 4, a: [ { loc: "B" }, { loc: "B" } ] } )
唯一单字段索引中缺失的文档字段
如果文档在唯一单字段索引中具有对索引字段的null
或缺失值,则索引存储该文档的null
值。由于唯一约束,单字段唯一索引只能包含一个包含索引条目中null
值的文档。如果有多个文档在其索引条目中具有null
值,则索引构建将因重复键错误而失败。
例如,一个集合在x
上有一个唯一单字段索引
db.collection.createIndex( { "x": 1 }, { unique: true } )
如果集合中不存在缺失字段x
的文档,则唯一索引允许插入没有字段x
的文档。
db.collection.insertOne( { y: 1 } )
然而,如果在集合中已经存在缺少字段 x
的文档,则无法插入缺少字段 x
的文档。
db.collection.insertOne( { z: 1 } )
由于违反了字段 x
的唯一约束,操作无法插入文档。
WriteResult({ "nInserted" : 0, "writeError" : { "code" : 11000, "errmsg" : "E11000 duplicate key error index: test.collection.$a.b_1 dup key: { : null }" } })
唯一复合索引中的缺失文档字段
如果一个文档在唯一复合索引中有一个或多个字段的值为 null
或缺失,则索引将存储文档索引条目中每个 null
或缺失字段的 null
值。由于唯一约束,唯一复合索引只允许一个索引条目中所有索引字段都为 null
值的文档。如果有多个索引条目中所有索引字段都为 null
值,则索引构建将因重复键错误而失败。MongoDB 允许在唯一复合索引中存在多个缺少字段的文档,只要每个索引条目都是唯一的。
例如,集合 students
在字段 name
、age
和 grade
上有一个唯一复合索引。
db.students.createIndex( { "name": 1, "age": -1, "grade": 1 }, { unique: true } )
如果集合中尚未包含相同的文档,则唯一复合索引允许插入以下所有缺少 grade
字段的文档。
db.students.insertMany( { "name": "Meredith", "age": 12 }, { "name": "Olivia", "age": 11 }, { "name": "Benjamin" } )
但是,您不能插入具有与集合中另一个文档相同的索引键(即 name
、age
和 grade
的值)的文档。
db.students.insertOne( { name: "Meredith", age: 12 } )
由于违反了字段 name
、age
和 grade
的唯一约束,操作无法插入文档。
WriteResult({ "nInserted" : 0, "writeError" : { "code" : 11000, "errmsg" : "E11000 duplicate key error collection: test.students index: name_1_age_-1_grade_1 dup key: { name: "Meredith", age: 12, grade: null } } } )
您也不能插入一个唯一但与现有索引条目共享索引键的文档。
db.students.insertOne( { name: "Olivia", "age": 11, "favorite color": "red"} )
由于违反了字段 name
、age
和 grade
的唯一约束,操作无法插入文档。
WriteResult({ "nInserted" : 0, "writeError" : { "code" : 11000, "errmsg" : "E11000 duplicate key error collection: test.students index: name_1_age_-1_grade_1 dup key: { name: "Olivia", age: 11, grade: null } } } )
唯一部分索引
部分索引仅索引满足特定过滤表达式的集合中的文档。如果您指定了 partialFilterExpression
和一个 唯一约束,则唯一约束仅适用于满足过滤表达式的文档。
具有唯一约束的部分索引在文档不满足过滤条件的情况下,不会阻止不满足唯一约束的文档的插入。例如,请参阅具有唯一约束的部分索引。
分片集群和唯一索引
您不能在 哈希索引 上指定唯一约束。
对于范围分片集合,以下索引可以是 唯一的
重要
当 _id
字段也是分片键时,分片集群仅在集群范围内强制执行 _id
字段的唯一约束。
如果 _id
字段不是分片键,或者它只是分片键的前缀,唯一约束仅适用于存储文档的分片。这意味着两个或多个文档可以有相同的 _id
值,前提是它们出现在不同的分片上。
例如,考虑一个具有分片键 {x: 1}
的分片集合,它跨越两个分片 A 和 B。因为 _id
键不是分片键,所以集合可以在分片 A 中有一个 _id
值为 1
的文档,并在分片 B 中有另一个 _id
值为 1
的文档。
当 _id
字段不是分片键时,MongoDB 预期应用程序在分片间强制执行 _id
值的唯一性。
唯一索引约束意味着
对于即将分片的集合,如果集合有多个唯一索引,除非分片键是所有唯一索引的前缀,否则无法对该集合进行分片。
对于已经分片的集合,除非分片键作为前缀包含在内,否则不能在其他字段上创建唯一索引。
唯一索引存储缺少索引字段的文档的null值;也就是说,缺少的索引字段被视为
null
索引键值的另一个实例。有关更多信息,请参阅唯一单字段索引中的缺失文档字段。
要在非分片键的字段上保持唯一性,请参阅任意字段上的唯一约束。
稀疏和非稀疏唯一索引
从MongoDB 5.0开始,可以在单个集合上存在具有相同键模式的唯一稀疏和非稀疏索引。
唯一和稀疏索引创建
此示例创建具有相同键模式但不同稀疏
选项的多个索引
db.scoreHistory.createIndex( { score : 1 }, { name: "unique_index", unique: true } ) db.scoreHistory.createIndex( { score : 1 }, { name: "unique_sparse_index", unique: true, sparse: true } )
基本和稀疏索引创建
您还可以使用相同的键模式创建带有和不带有稀疏选项的基本索引
db.scoreHistory.createIndex( { score : 1 }, { name: "sparse_index", sparse: true } ) db.scoreHistory.createIndex( { score : 1 }, { name: "basic_index" } )
具有重复键模式的基准和唯一索引
从MongoDB 5.0版本开始,基准索引和唯一索引可以具有相同的键模式。
这种键模式的重复允许向已经索引的字段添加唯一索引。
在以下示例中
创建一个具有键模式 { score : 1 }
的基本索引并插入三个文档。
db.scoreHistory.createIndex( { score : 1 }, { name: "basic_index" } ) db.scoreHistory.insert( { score : 1 } ) db.scoreHistory.insert( { score : 2 } ) db.scoreHistory.insert( { score : 3 } )
创建一个具有相同键模式 { score : 1 }
的唯一索引。
db.scoreHistory.createIndex( { score : 1 }, { name: "unique_index", unique: true } )
尝试插入一个重复的 score
文档,由于唯一索引而失败。
db.scoreHistory.insert( { score : 3 } )