Docs 菜单
Docs 主页
/ /

Unique Indexes

唯一索引可确保索引字段的值是唯一的。

  • 唯一的单字段索引可确保给定字段的值最多出现一次。

  • 唯一复合索引可确保索引键值的任何给定组合最多只出现一次。

默认下,创建新集合时, MongoDB会在 _id字段上创建唯一索引。

您可以在用户界面中为 MongoDB Atlas 中托管的部署创建和管理唯一索引

要在MongoDB Shell中创建唯一索引,请使用db.collection.createIndex() 方法,并将unique 选项设立为true

db.collection.createIndex(
{ <field>: <sortOrder> },
{ unique: true }
)

要创建唯一索引,请参阅:

如果集合已包含违反唯一性约束的数据,则MongoDB无法直接创建唯一索引。要在包含重复值的集合上创建唯一索引,可以使用 prepareUnique 选项。

要在包含重复值的集合上使用 prepareUnique,请执行以下步骤:

  1. 在要实施唯一性的字段或多个字段上创建非唯一索引。

  2. 使用 命令将索引的collMod 设立为prepareUnique true

    prepareUnique设立为 true 时,将对索引字段的所有新写入强制执行唯一约束。现有的重复值不会被删除。

  3. 解析索引字段中任何现有的重复值。

  4. 使用 collMod 命令使索引。

如果您已经有一个包含重复值的数据集要进行去重,并且需要确保在清理进程中不会添加新的重复值,则 prepareUnique功能非常有用。

有关使用prepareUnique 的完整示例,请参阅将现有索引转换为唯一索引。

对于副本集和分片集群,使用滚动过程创建唯一索引要求您在该过程中停止对集合的所有写入。如果在此过程中无法停止对集合的所有写入,请勿使用滚动过程。相反,要为集合构建唯一索引,您必须执行以下操作之一:

  • 在副本集的主节点上运行 db.collection.createIndex()

  • mongos 上为分片集群运行 db.collection.createIndex()

唯一约束适用于集合中的独立文档。换言之,唯一索引可以防止独立文档为该索引键具有相同的值。

由于该约束适用于独立文档,因此对于唯一多键索引,只要某文档的索引键值与其他文档的索引键值不重复,该文档就可能包含导致重复索引键值的数组元素。在这种情况下,重复的索引条目仅插入索引一次。

示例,考虑一个 inventory集合,其中包含以下表示产品及其股票地点的文档:

db.inventory.insertMany( [
{ _id: 1, product: "pencils", inventory: [ { warehouse: "NYC", quantity: 5 }, { quantity: 10 } ] },
{ _id: 2, product: "pens", inventory: [ { warehouse: "NYC" }, { quantity: 5 } ] },
{ _id: 3, product: "markers", inventory: [ { warehouse: "NYC", quantity: 10 } ] }
] )

inventory.warehouseinventory.quantity 上创建唯一复合多键索引:

db.products.createIndex( { "inventory.warehouse": 1, "inventory.quantity": 1 }, { unique: true } )

如果此集合中没有其他文档的索引键值为 { "inventory.warehouse": "LA", "inventory.quantity": null },则该唯一索引允许将以下文档插入集合:

db.products.insertOne( { _id: 4, product: "Sprocket", inventory: [ { warehouse: "LA" }, { warehouse: "LA" } ] } )

对于唯一单字段索引中的索引字段,如果某文档存在 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 在字段 nameagegrade 上具有唯一复合索引:

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" }
] )

但是无法插入与集合中另一份文档具有相同索引键(nameagegrade 的值)的文档。

db.students.insertOne( { name: "Meredith", age: 12 } )

该操作无法插入文档,因为它违反 nameagegrade 字段值的唯一性约束:

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"} )

该操作无法插入文档,因为它违反 nameagegrade 字段值的唯一性约束:

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 值的唯一性,示例,使用唯一标识符填充 _id字段。

唯一索引约束意味着:

  • 对于要分片的集合,如果该集合有多个唯一索引,则无法对该集合分片,除非分片键是所有唯一索引的前缀。

  • 对于已分片的集合,除非包含分片键作为前缀,否则无法在其他字段上创建唯一索引。

  • 唯一索引会为缺少索引字段的文档存储空值;即缺失的索引字段被视为 null索引键值的另一个实例。 有关更多信息,请参阅唯一单字段索引中缺少文档字段。

要保持分片键字段的唯一性,请参阅任意字段的唯一性约束。

从 MongoDB 5.0 开始,具有相同键模式唯一稀疏唯一非稀疏索引可以存在于同一个集合中。

此示例将使用相同的键模式和不同的 sparse 选项来创建多个索引:

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" } )

基本索引和唯一索引可以使用相同的键模式。

这种键模式的重复允许向已索引的字段添加唯一索引。

在本例中:

使用键模式 { 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 } )

后退

数据过期

获得技能徽章

免费掌握“索引设计基础”!

了解详情

在此页面上