是否有可能为两个不同集合中的文档生成相同的Mongo ObjectId ?我知道这绝对不太可能,但这有可能吗?
不太具体,我问这个问题的原因是,在我正在开发的一个应用程序中,我们展示了民选官员的公开资料,我们希望将他们转化为我们网站的正式用户。我们为用户和目前不是我们网站成员的当选官员提供了单独的收藏。还有其他各种文档,包含关于当选官员的各种数据,这些数据都映射到使用他们的当选官员ObjectId的人。
创建帐户后,我们仍然突出显示与当选官员相关的数据,但它们现在也是用户集合的一部分,具有相应的用户ObjectId,以将他们的配置文件映射到与我们的应用程序的交互。
几个月前,我们已经开始将应用程序从MySql转换为Mongo,在过渡过程中,我们存储了这两种数据类型的遗留MySql id,现在我们也开始在用户文档中存储民选的官方Mongo ObjectId,以映射回民选的官方数据。
我正在考虑只是指定新的用户ObjectId作为以前选举的官方ObjectId,以使事情更简单,但要确保它不可能与任何现有的用户ObjectId发生冲突。
谢谢你的洞察力。
编辑:在发布这个问题后不久,我意识到我提出的解决方案不是一个很好的主意。最好保持我们现有的模式,并链接到用户文档中被选中的官方“_id”。
对于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空间,因此将保证是唯一的。从“预期”切换到“注册”将是一个简单的翻转字段…
简短的回答
只是对你最初的问题添加一个直接的回答:是的,如果你使用BSON对象ID生成,那么对于大多数驱动程序,ID几乎肯定会在集合中是唯一的。下面是“几乎肯定”的意思。
长回答
Mongo DB驱动生成的BSON对象ID在集合中很可能是唯一的。这主要是因为ID的最后3个字节,对于大多数驱动程序来说,这是通过静态递增计数器生成的。该计数器是集合独立的;这是全球性的。例如,Java驱动程序使用一个随机初始化的静态AtomicInteger。
那么,为什么在Mongo文档中,他们说id“极有可能”是唯一的,而不是直接说它们将是唯一的?三种可能发生的情况下,你不会得到一个唯一的ID(请让我知道如果有更多):
在讨论之前,回想一下BSON对象ID包括:
[4 bytes seconds since epoch, 3 bytes machine hash, 2 bytes process ID, 3 bytes counter]
以下是三种可能性,你可以自己判断被骗的可能性有多大:
1)计数器溢出:计数器内有3个字节。如果您恰好在一秒钟内在同一台机器上的同一个进程中插入超过16,777,216(2^24)个文档,那么您可能会溢出递增的计数器字节,并最终得到两个Object id,它们共享相同的时间、机器、进程和计数器值。
2)计数器非递增:一些Mongo驱动程序使用随机数而不是递增的计数器字节数。在这些情况下,生成非唯一ID的几率为1/16,777,216,但前提是这两个ID是在同一秒内(即在ID的时间部分更新到下一秒之前)在同一台机器上的同一进程中生成的。
3)机器和处理哈希到相同的值。在某些极不可能的情况下,机器ID和进程ID值可能映射到两台不同机器的相同值。如果发生这种情况,并且在同一时间内两台不同机器上的两个计数器生成相同的值,那么最终将得到重复的ID。
以下是需要注意的三种情况。情况1和3似乎不太可能,如果使用正确的驱动程序,情况2是完全可以避免的。你必须检查驱动程序的来源才能确定。
如果有人有重复的Mongo objectid的问题,你应该知道,尽管dup发生在Mongo本身的可能性不大,它是有可能有重复的_id的生成与PHP在Mongo。
对我来说,这种情况经常发生的用例是当我循环一个数据集并试图将数据注入到一个集合中时。
保存注入数据的数组必须在每次迭代时显式重置—即使您没有指定_id值。由于某些原因,INSERT进程将Mongo _id添加到数组中,就像它是一个全局变量一样(即使数组没有全局作用域)。即使您在一个单独的函数调用中调用插入(通常不希望数组的值持久化回调用函数),这也会影响您。
有三个解决方案:
您可以从数组中unset() _id字段
每次遍历数据集时,都可以使用array()重新初始化整个数组
您可以自己显式地定义_id值(注意定义它时不要自己生成dup)。
我的猜测是,这是PHP界面中的一个bug,与Mongo没有太大的问题,但如果你遇到这个问题,只要取消设置_id,你就应该没事了。