>>>>>>> cn
翻译或纠错本页面

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

On this page

概述

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

MongoDB does not support creating new unique indexes in sharded collections and will not allow you to shard collections with unique indexes on fields other than the _id field.

If you need to ensure that a field is always unique in a sharded collection, there are three options:

  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.createIndex( { "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: createIndex() and shardCollection.

注意事项

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

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

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

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

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

This approach is particularly useful for the``_id`` field, which must be unique: for collections where you are not sharding by the _id field the application is responsible for ensuring that the _id field is unique.