是否可以在两个不同的集合中生成重复的Mongo ObjectId?

时间:2011-01-13 05:58:53

标签: mongodb database nosql

是否可以为两个不同集合中的文档生成相同的Mongo ObjectId?我意识到这绝对不太可能,但有可能吗?

没有太具体,我问的原因是,通过我正在处理的应用程序,我们会显示当选官员的公开个人资料,我们希望将其转换为我们网站的完全成熟用户。我们为用户和当前不是我们网站成员的当选官员提供单独的收藏。还有各种其他文件,其中包含有关当选官员的各种数据,这些数据都使用他们选出的官方ObjectId映射回该人。

创建帐户后,我们仍会突出显示与当选官员相关联的数据,但他们现在也是用户集合的一部分,并且相应的用户ObjectId将其个人资料映射到与我们的应用程序的交互。

几个月前我们已经开始将我们的应用程序从MySql转换为Mongo,当我们处于转换状态时,我们存储了这两种数据类型的遗留MySql ID,我们现在也开始存储当选的官方Mongo ObjectId在用户文档中映射回选定的官方数据。

我正在考虑将新用户ObjectId指定为先前选定的官方ObjectId,以使事情更简单,但是要确保不可能与任何现有用户ObjectId发生冲突。

感谢您的见解。

编辑:发布此问题后不久,我意识到我提出的解决方案不是一个好主意。最好只保留我们现有的架构,并链接到用户文档中选定的官方'_id'。

4 个答案:

答案 0 :(得分:289)

简答

只需添加对您的初始问题的直接回复:是,如果您使用BSON对象ID生成,那么<​​em>对于大多数驱动程序这些ID几乎肯定会在集合中是唯一的。请参阅下文,了解“几乎可以肯定”的含义。

长答案

Mongo DB驱动程序生成的BSON对象ID很可能在集合中是唯一的。这主要是因为ID的最后3个字节,大多数驱动程序的 是通过静态递增计数器生成的。该计数器与收集无关;它是全球性的。例如,Java驱动程序使用随机初始化的静态AtomicInteger。

那么,为什么在Mongo文档中,他们是否说“ID很可能”是独一无二的,而不是直截了当地说它们将是独一无二的?如果您没有获得唯一ID,可能会出现三种可能性(如果有更多信息,请告诉我们):

在此讨论之前,请回想一下BSON对象ID包含:

[epoch以来的4个字节,3个字节的机器散列,2个字节的进程ID,3个字节的计数器]

以下是三种可能性,因此您可以自己判断获得欺骗的可能性:

1)计数器溢出:计数器中有3个字节。如果你碰巧在同一台机器上,在同一个机器上一秒钟内插入了超过16,777,216(2 ^ 24)个文件,那么你可能会溢出增量计数器字节并最终得到两个共享同一时间的对象ID,机器,过程和计数器值。

2)计数器不递增:一些Mongo驱动程序使用随机数而不是递增计数字节的数字。在这些情况下,有1 / 16,777,216的机会生成非唯一ID,但仅当这两个ID在同一秒内生成时(即在ID的时间段更新到下一秒之前),在同一秒内生成机器,在同一个过程中。

3)机器和进程哈希到相同的值。在某些极不可能的情况下,机器ID和进程ID值可以映射到两个不同机器的相同值。如果发生这种情况,同时两台不同机器上的两个计数器在同一秒内生成相同的值,那么您最终会得到一个重复的ID。

这是需要注意的三个方案。场景1和3似乎不太可能,如果您使用正确的驱动程序,则场景2完全可以避免。您必须检查驱动程序的来源以确定。

答案 1 :(得分:13)

ObjectIds以类似于UUID的方式在客户端生成,但具有一些更好的属性,可以存储在数据库中,例如大致增加顺序并免费编码创建时间。对于您的用例而言,关键在于它们旨在保证高概率的唯一性,即使它们是在不同的机器上生成的。

现在,如果你指的是一般的_id字段,我们不需要跨集合的唯一性,因此重用旧的_id是安全的。举个具体的例子,如果你有两个集合,colorsfruits,它们可以同时拥有像{_id: 'orange'}这样的对象。

如果您想了解有关如何创建ObjectId的更多信息,请参阅以下规范:http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification

答案 2 :(得分:11)

如果有人遇到重复的Mongo ObjectID问题,你应该知道,尽管Mongo本身不太可能发生重复,但是有可能在Mongo中使用PHP生成重复的_id。

我经常发生这种情况的用例是当我循环遍历数据集并尝试将数据注入集合时。

必须在每次迭代时显式重置保存注入数据的数组 - 即使您未指定_id值。出于某种原因,INSERT进程将Mongo _id添加到数组中,就像它是一个全局变量一样(即使数组没有全局范围)。即使您在单独的函数调用中调用插入,您通常也希望数组的值不会持久地返回到调用函数,这会对您产生影响。

有三种解决方案:

  1. 您可以unset()数组中的_id字段
  2. 每次循环数据集时,都可以使用array()重新初始化整个数组
  3. 您可以自己明确定义_id值(注意以自己不生成重复的方式定义它)。
  4. 我的猜测是,这是PHP界面中的一个错误,而不是Mongo的问题,但是如果遇到这个问题,只需取消设置_id即可。你应该没问题。

答案 3 :(得分:-6)

无法保证跨集合的ObjectId唯一性。即使它在概率上非常不可能,但这将是一个非常糟糕的应用程序设计,它依赖于各个集合的_id唯一性。

可以在mongo shell中轻松测试:

MongoDB shell version: 1.6.5
connecting to: test
> db.foo.insert({_id: 'abc'})
> db.bar.insert({_id: 'abc'})
> db.foo.find({_id: 'abc'})
{ "_id" : "abc" }
> db.bar.find({_id: 'abc'})
{ "_id" : "abc" }
> db.foo.insert({_id: 'abc', data:'xyz'})
E11000 duplicate key error index: test.foo.$_id_  dup key: { : "abc" }

所以,绝对不要依赖于_id在集合中是唯一的,并且由于你不控制ObjectId生成函数,所以不要依赖它。

可以创建更像uuid的东西,如果你手动完成,你可以更好地保证唯一性。

请记住,您可以将不同“类型”的对象放在同一个集合中,那么为什么不将两个“表”放在同一个集合中。它们将共享相同的_id空间,因此将保证唯一。从“预期”转换为“注册”将是一个简单的场地翻转...