OPTIONS
翻译或纠错本页面

确保分片集合中唯一字段的唯一性

概述

在创建索引时增加 unique 用来保证在一个 collection 中字段的唯一性.但是对于 分片集合这样的索引并不能保证字段的唯一性 ,因为插入和索引操作对于每个分片都是本地操作.

在分片集合中,不允许创建不包含全部片键的唯一索引.

如果需要在分片集合中保证字段的唯一性,有三种选项:

  1. 使用 片键 保证唯一性.

    MongoDB 可以 保证 shard key 的唯一性.对于复合片键,可以创建包含全部片键的唯一索引.

    哈希索引 不能有唯一性约束.

  2. 使用第二个集合保证唯一性.

    创建另一个只包含唯一字段的不分片的集合,每次在写入主集合之前先将唯一字段的数据试图写入这个集合,若写入失败,则表示有冲突.

    如果你的数据量比较小,可以不使用分片,就可以创建多个唯一索引了.

  3. 使用本身便能保证唯一性的标识符

    一般情况下像 ObjectId 这样的唯一标识符(即 UUID)是可以保证唯一性的.

规程

片键上的唯一约束

过程

要使用 unique 条件进行分片,需要像下面这样执行 shardCollection :

db.runCommand( { shardCollection : "test.users" , key : { email : 1 } , unique : true } );

记住 _id 字段总是唯一的,默认情况下,MongoDB会将 ObjectId 写到 _id 字段.然而,你也可以将自己生成的值写到 _id 字段并用这个字段作为片键.使用以下操作将 _id 用作片键:

db.runCommand( { shardCollection : "test.users" } )

限制

  • 使用这种方法你只能在单字段上保证唯一性.

  • 如果使用了复合片键,则只能在包含了全部片键字段的字段组合中保持唯一性.

在大多数情况下,能够提供 写扩展 特性,:ref:查询隔离 <sharding-shard-key-query-isolation> 特性和 高基数能力 特性的复合片键.大多数情况下,这个片键的组合并不需要保证唯一性,集合中需要使用其他实现字段的唯一性.

任意字段的唯一性.

如果不能使用片键保证唯一性或者要保证多个字段的唯一性,必须创建第二个 collection 作为 “代理集合”,代理集合需要包含对原始文档集合的引用(比如对 ObjectId 的引用),以及唯一字段.

如果必须在 “代理” 集合上分片,使用 上面的教程 用唯一字段做片键.如果不分片,可以直接在代理集合上创建多个唯一索引.

过程

参考以下使用”代理索引”的场景:

{
  "_id" : ObjectId("...")
  "email" ": "..."
}

_id 字段保存了主集合中的 ObjectId , email 字段是想要保证唯一性的字段.

要在代理集合分片,使用以下操作使用 email 字段做 shard key:

db.runCommand( { shardCollection : "records.proxy" ,
                 key : { email : 1 } ,
                 unique : true } );

如果不需要在代理集合分片,使用以下命令在 email 字段创建唯一索引:

db.proxy.ensureIndex( { "email" : 1 }, { unique : true } )

如果不需要在代理集合分片,可以在这个集合上创建多个唯一索引.

插入数据时,在 JavaScript shell: 中使用以下过程:

db = db.getSiblingDB('records');

var primary_id = ObjectId();

db.proxy.insert({
   "_id" : primary_id
   "email" : "example@example.net"
})

// if: the above operation returns successfully,
// then continue:

db.information.insert({
   "_id" : primary_id
   "email": "example@example.net"
   // additional information...
})

必须首先在代理集合中插入一个数据,如果插入成功了,表明 email 字段是唯一的,之后就可以将其余的文档插入到 information 集合中.

参见

The full documentation of: ensureIndex() and shardCollection.

注意事项

  • 你的应用必须能够捕获插入代理集合时产生的错误,并保证两个集合之前的一致性.

  • 如果代理集合需要分片,必须使用你想要保证唯一性的字段做片键.

  • 在对代理集合使用分片的情况下,如果想要保证多个字段的唯一性.必须对 每个保证唯一性的字段 都创建一个代理集合.如果使用一个代理集合用来确保多个字段的唯一性,这个代理集合 不能够 进行分片.

使用本身可以保证唯一性的标识符

保证唯一性最好的方法是创建自身可以保证唯一性的标识符(UUID),比如MongoDB的 ObjectId 值.

这个方法对特别适用于 _id ,在不以 _id 为片键进行分片时,应用程序依然可以保证 _id 的唯一性.