如果无法使用唯一字段作为分片键,或者需要实施多个字段的唯一性,请创建另一个集合以充当代理集合。
关于此任务
仅当唯一索引位于分片键上或包含分片键作为前缀时,分片集群才会跨分片实施唯一约束。代理集合可以绕过该限制:代理集合包含对原始文档的引用和唯一键,并且您的应用程序会首先写入代理集合。
使用代理集合时,请考虑以下事项:
如果代理集合需要分片,则必须对要强制唯一性的单个字段进行分片。
每个字段都需要自己的代理集合。要使用分片的代理集合实施多个字段的唯一性,必须为每个字段设置一个代理集合。如果在单个代理集合上创建多个唯一索引,则无法对代理集合分片。
这种方法需要在应用程序中生成
_id值,而不是让MongoDB在插入文档时自动创建值,以便两个文档股票相同的_id。
开始之前
标识需要唯一约束但不能作为分片键一部分的字段。
确认现有文档不违反该字段的唯一性约束。
步骤
考虑一个存储用户信息的集合records。字段email 不是分片键,但必须是唯一的。 proxy集合包含以下字段:
{ "_id" : ObjectId("..."), "email" : "<string>"}1
2
写入ACID 事务中的两个集合。
将包含目标字段和生成的唯一ID 的文档插入 proxy集合,然后将完整文档插入 records集合。在一个ACID 事务中运行两个插入操作,以便MongoDB要么应用这两个写入,要么都不应用:
// Start a sessionsession = db.getMongo().startSession( { readPreference: { mode: "primary" } } );proxy = session.getDatabase( db.getName() ).proxy;records = session.getDatabase( db.getName() ).records;
var primary_id = ObjectId();
// Start a transactionsession.startTransaction( { readConcern: { level: "local" }, writeConcern: { w: "majority" } } );
// Operations inside the transactiontry { proxy.insertOne( { "_id" : primary_id, "email" : "example@example.net" } );
records.insertOne( { "_id" : primary_id, "email" : "example@example.net", // additional information... } );} catch (error) { // A duplicate email value fails the proxy insert and // cancels the transaction session.abortTransaction(); throw error;}// Commit the transaction using write concern set at transaction startsession.commitTransaction();session.endSession();如果 email 值已存在于 proxy集合中,则唯一索引会拒绝插入,ACID 事务会中止,因此不会向 records 添加任何文档。