是否可以为两个不同集合中的文档生成相同的 Mongo ObjectId?我意识到这绝对是不可能的,但有可能吗?
无需太具体,我问的原因是,通过我正在开发的应用程序,我们显示了民选官员的公开资料,我们希望将他们转变为我们网站的正式用户。 We have separate collections for users and the elected officials who aren't currently members of our site. There are various other documents containing various pieces of data about the elected officials that all map back to the person using their elected official ObjectId.
After creating the account we still highlight the data that's associated to the elected official but they now also are a part of the users collection with a corresponding users ObjectId to map their profile to interactions with our application.
We had begun converting our application from MySql to Mongo a few months ago and while we're in transition we store the legacy MySql id for both of these data types and we're also starting to now store the elected official Mongo ObjectId in the users document to map back to the elected official data.
我正在考虑只是将新用户 ObjectId 指定为先前选出的官方 ObjectId 以使事情变得更简单,但想确保不可能与任何现有用户 ObjectId 发生冲突。
感谢您的洞察力。
编辑:发布这个问题后不久,我意识到我提出的解决方案不是一个好主意。最好只保留我们现有的当前模式,并在用户文档中链接到选定的官方“_id”。
简答
只是直接回答您最初的问题:是的,如果您使用 BSON 对象 ID 生成,那么对于大多数驱动程序来说,ID 几乎肯定会在集合中是唯一的。请参阅下文了解“几乎可以肯定”的含义。
长答案
Mongo DB 驱动程序生成的 BSON 对象 ID 很可能在集合中是唯一的。这主要是因为 ID 的最后 3 个字节,对于大多数驱动程序来说,它是通过静态递增计数器生成的。该计数器是独立于收集的;它是全球性的。例如,Java 驱动程序使用随机初始化的静态 AtomicInteger。
那么,为什么在 Mongo 文档中,他们会说 ID“很可能”是唯一的,而不是直截了当地说它们将是唯一的?在您无法获得唯一 ID 的情况下,可能会出现三种可能性(如果还有更多,请告诉我):
在讨论之前,回想一下 BSON 对象 ID 包括:
[自纪元以来的 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 是完全可以避免的。您必须检查驱动程序的来源才能确定。
ObjectId 以类似于 UUID 的方式在客户端生成,但具有一些更好的存储在数据库中的属性,例如粗略地增加顺序和免费编码它们的创建时间。您的用例的关键在于,它们旨在保证高概率的唯一性,即使它们是在不同的机器上生成的。
现在,如果您通常指的是 _id 字段,我们不需要跨集合的唯一性,因此重用旧的 _id 是安全的。举个具体的例子,如果您有两个集合 colors
和 fruits
,那么它们可能同时有一个像 {_id: 'orange'}
这样的对象。
如果您想了解有关如何创建 ObjectId 的更多信息,请参阅以下规范:http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification
如果有人遇到重复 Mongo ObjectID 的问题,您应该知道,尽管 Mongo 本身不太可能发生重复,但在 Mongo 中使用 PHP 生成重复的 _id 是可能的。
对我来说,这种情况经常发生的用例是当我循环数据集并尝试将数据注入集合时。
保存注入数据的数组必须在每次迭代时显式重置 - 即使您没有指定 _id 值。出于某种原因,INSERT 进程将 Mongo _id 添加到数组中,就好像它是一个全局变量一样(即使数组没有全局范围)。即使您在单独的函数调用中调用插入,这也会影响您,您通常希望数组的值不会持续回调用函数。
对此有三种解决方案:
您可以 unset() 数组中的 _id 字段您可以在每次遍历数据集时使用 array() 重新初始化整个数组您可以自己显式定义 _id 值(注意以您不这样做的方式定义它) t 自己生成副本)。
我的猜测是这是 PHP 界面中的一个错误,与 Mongo 无关,但如果你遇到这个问题,只需取消设置 _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 空间,因此可以保证是唯一的。从“预期”切换到“注册”将是一个简单的领域翻转......
ObjectId