考虑到 UUID rfc 4122(16 字节)比 MongoDB ObjectId(12 字节)大得多,我试图找出它们的碰撞概率比较。
我知道这不太可能发生,但在我的情况下,大多数 id 将在大量移动客户端中生成,而不是在有限的一组服务器中生成。我想知道在这种情况下,是否有合理的担忧。
与所有 id 由少量客户端生成的正常情况相比:
- 自文档创建以来,检测冲突可能需要几个月的时间
- ID 是从更大的客户群中生成的
- 每个客户端的 ID 生成率较低
在我的情况下,大多数 id 将在大量移动客户端中生成,而不是在有限的一组服务器中生成。我想知道在这种情况下是否存在合理的担忧。
对我来说,这听起来像是非常糟糕的架构。您使用的是两层架构吗?为什么移动客户端可以直接访问数据库?您真的要依赖基于网络的安全性吗?
无论如何,关于碰撞概率的一些审议:
UUID 和 ObjectId 都不依赖于它们的绝对大小,即两者都不是随机数,但它们遵循一种尝试系统地降低冲突概率的方案。在ObjectIds 的情况下,它们的结构是:
这意味着,与 UUID 不同,ObjectId 是单调的(除了在一秒钟内),这可能是它们最重要的属性。单调索引将使 B-Tree 被更有效地填充,它允许按 id 分页并允许按 id 进行“默认排序”以使您的游标稳定,当然,它们带有易于提取的时间戳。这些是您应该注意的优化,它们可能是巨大的。
正如您从其他 3 个组件的结构中看到的那样,如果您在单个进程上执行 > 1k 插入/秒(实际上不可能,甚至来自服务器),或者机器数量增加,则冲突变得非常可能超过 10 个(参见生日问题),或者如果单台机器上的进程数变得太大(再说一遍,这些不是随机数,但它们在一台机器上确实是唯一的,但它们必须缩短为两个字节)。
自然,为了发生冲突,它们必须在所有这些方面都匹配,因此即使两台机器具有相同的机器哈希,它仍然需要客户端在完全相同的秒和相同的过程中插入相同的计数器值id,但是是的,这些值可能会发生冲突。
让我们看一下文档中“ObjectId”的规范:
概述
ObjectId 是一个 12 字节的 BSON 类型,使用以下方法构造:
- 一个 4 字节的值,表示自 Unix 纪元以来的秒数,
- 一个 3 字节的机器标识符,
- 一个 2 字节的进程 ID,以及
- 一个 3 字节的计数器,从一个随机值开始。
因此,让我们在“移动客户端”的背景下考虑这一点。
注意:这里的上下文并不意味着使用“移动客户端”到数据库的“直接”连接。不应该这样做。但是“_id”生成可以很简单地完成。
所以要点:
“自纪元以来的秒数”的值。每个请求将是相当随机的。因此,仅对该组件的碰撞影响最小。虽然是“秒”。
“机器标识符”。所以这是一个不同的客户产生_id
价值。这消除了进一步“碰撞”的可能性。
“进程 ID”。因此,在种子可以访问的地方(应该是),那么生成的_id
就有更多的机会避免碰撞。
“随机值”。因此,另一个“客户端”以某种方式设法生成所有与上述相同的值,并且仍然设法生成相同的随机值。
底线是,如果这不是一个足以令人信服的论据来消化,那么只需提供您自己的“uuid”条目作为“主键”值。
但是恕我直言,考虑到这里的碰撞方面非常广泛,这应该是一个公平令人信服的论点。至少可以说。
完整的主题可能只是有点“过于宽泛”。但我希望这能将考虑从“不太可能”转移到更具体的事情上。