在两个不同的集合中生成重复的Mongo ObjectId的可能性?

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

没有太具体的问题,我要问的原因是,在我正在开发的一个应用程序中,我们展示了民选官员的公开档案,我们希望他们能够转换成我们网站的全面用户。 我们为用户和当选的官员单独collections,他们目前不是我们网站的成员。 还有各种其他文件,其中包含关于当选官员的各种数据,全部使用他们当选的官方ObjectId映射回该人。

创build帐户后,我们仍然突出显示与当选官员关联的数据,但现在他们也是用户集合的一部分,并具有相应的用户ObjectId,以将其configuration文件映射到与应用程序的交互。

几个月前,我们已经开始将我们的应用程序从MySql转换到Mongo,而在转换过程中,我们为这两种数据types存储了传统的MySql ID,而且现在我们也开始将选定的官方Mongo ObjectId存储在用户将文件映射回选出的官方数据。

我正在思索,只是指定新的用户ObjectId作为以前选定的官方ObjectId,使事情变得更简单,但要确保它不可能与任何现有的用户ObjectId发生冲突。

感谢您的洞察力。

编辑:发布这个问题后不久,我意识到我提出的解决scheme不是一个好主意。 最好保留我们现有的模式,并链接到用户文档中选出的官方“_id”。

简答

只是为了最初的问题添加一个直接的回答:是的,如果你使用BSON对象ID的生成,那么对于大多数驱动程序来说 ,ID在集合中几乎肯定是唯一的。 请参阅下面的“几乎可以肯定”的含义。

长答案

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

那么为什么在Mongo文档中,他们是否认为ID很有可能是独一无二的,而不是直接说它们是独一无二的? 如果您不能获得唯一的身份证,则可能会发生三种可能性(请让我知道是否还有更多):

在讨论之前,回想一下,BSON对象ID由以下部分组成:

[历元4字节秒,3字节机器散列,2字节进程ID,3字节计数器]

这里有三种可能性,所以你自己判断有可能得到一个骗局:

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

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

3)机器和进程散列值相同。 在某些极不可能的情况下,机器标识和过程标识值可能映射到两台不同机器的相同值。 如果发生这种情况,同时两个不同机器上的两个计数器在同一秒钟内产生相同的值,那么您将得到一个重复的ID。

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

ObjectIds是以类似于UUID的方式在客户端生成的,但是在数据库中存储一些更好的属性,比如大致增加顺序并免费编码它们的创build时间。 你的使用案例的关键在于它们被devise为即使在不同的机器上生成它们也能保证很高的可能性。

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

如果你想知道更多关于如何创buildObjectIds,这里是规范: http : //www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification

如果任何人有重复的Mongo ObjectID的问题,你应该知道,尽pipe在Mongo本身发生dups的可能性不大,但是在Mongo中可能会有PHP生成的重复的_id。

对我而言,这种情况经常发生的用例是当我循环访问数据集并尝试将数据注入到集合中时。

保存注入数据的数组必须在每次迭代中显式重置 – 即使您没有指定_id值。 出于某种原因,INSERT进程将Mongo _id添加到数组,就好像它是一个全局variables(即使该数组没有全局作用域)。 即使你正在一个单独的函数调用中调用插入,这通常会期望数组的值不会保留callback用函数,这会影响到你。

有三种解决scheme:

  1. 您可以从数组中unset() _id字段
  2. 每次循环访问数据集时,都可以使用array()重新初始化整个数组
  3. 你可以自己明确地定义_id值(注意定义它,以免自己产生愚蠢)。

我的猜测是,这是PHP界面中的一个错误,与Mongo没有多大关系,但是如果遇到这个问题,只需取消_id即可。

不能保证所有集合的ObjectId唯一性。 即使概率极不可能,这也是一个非常糟糕的应用程序devise,它依赖于各个集合的_id独特性。

人们可以很容易地在mongo shell中testing这个:

 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生成函数,所以不要依赖它。

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

请记住,您可以将不同“types”的对象放在同一个集合中,为什么不把两个“表”放在同一个集合中? 他们将分享相同的_id空间,因此,将保证独特。 从“准”转换到“注册”将是一个简单的翻转字段…