大型分布式系统中ObjectId与UUID的冲突概率

Sys*_*ank 23 uuid guid mongodb rfc4122

考虑到UUID rfc 4122(16字节)远大于MongoDB ObjectId(12字节),我试图找出它们的碰撞概率如何比较.

我知道这种情况不太可能,但在我的情况下,大多数ID将在大量移动客户端中生成,而不是在有限的服务器集中生成.我想知道在这种情况下,是否存在合理的担忧.

与通常由少数客户生成所有ID的情况相比:

  • 自文档创建以来,可能需要数月才能检测到碰撞
  • ID是从更大的客户群生成的
  • 每个客户端的ID生成率都较低

mne*_*syn 29

在我的情况下,大多数ID将在大量移动客户端中生成,而不是在有限的服务器集中生成.我想知道在这种情况下,是否存在合理的担忧.

这对我来说听起来非常糟糕.您使用的是双层架构吗?为什么移动客户端可以直接访问数据库?您真的想依靠基于网络的安全性吗?

无论如何,关于碰撞概率的一些讨论:

UUID和ObjectId都不依赖于它们的绝对大小,即两者都不是随机数,但它们遵循试图系统地降低冲突概率的方案.在ObjectIds的情况下,它们的结构是:

  • 自unix时代以来4字节秒
  • 3字节机器ID
  • 2字节进程ID
  • 3字节计数器

这意味着,与UUID相反,ObjectIds是单调的(除了在一秒内),这可能是他们最重要的属性.单调索引将使B-Tree更有效地填充,它允许按id进行分页,并允许通过id进行"默认排序"以使您的游标稳定,当然,它们带有易于提取的时间戳.这些是你应该意识到的优化,它们可能是巨大的.

从其他3个组件的结构中可以看出,如果您在单个进程上执行> 1k insert/s(实际上不可能,甚至不是来自服务器),或者如果机器数量增加,则冲突很可能发生过去大约10(看生日问题),或者如果一台机器上的进程数量增长得太大(那么,那些不是随机数,但它们在机器上真的是唯一的,但它们必须缩短为两个字节).

当然,对于发生冲突,它们必须在所有这些方面匹配,因此即使两台机器具有相同的机器哈希,它仍然需要客户端在完全相同的秒和相同的过程中插入具有相同计数器值的客户端id,但是,这些值可能会发生碰撞.

  • 从客户端生成ID是完全有效的情况,并不意味着完全访问数据库.如果你这样做,你肯定不能使用ObjectIds,因为如果你有数十或数十万个客户端生成它们会有严重的冲突问题.我不相信ObjectIds,因为它很容易找到案例,即使它们需要特定条件,也可能发生冲突. (5认同)
  • @mnemosyn有用的答案,但是,我不明白为什么每个进程1k insert/s已经是一个问题.你会认为计数器在同一秒内每个"请求"递增1,并在下一秒开始时重置为零.但是使用3个字节,您可以表示比1k更大的数字.我在这里错过了什么? (2认同)

Nei*_*unn 13

让我们从文档中查看"ObjectId"的规范:

概观

ObjectId是一个12字节的BSON类型,使用以下构造:

  • 一个4字节的值,表示自Unix纪元以来的秒数,
  • 一个3字节的机器标识符,
  • 一个2字节的进程ID,和
  • 一个3字节的计数器,以随机值开始.

因此,让我们在"移动客户端" 的背景下考虑这一点.

注:这里的背景并不能意味着使用"移动客户端"到数据库"直接"连接.这应该不会做.但是"_id"代可以很简单地完成.

所以要点:

  1. "自纪元以来的秒数"的价值.根据请求,这将是相当随机的.因此对该组件的影响最小.虽然在"秒".

  2. "机器标识符".所以这一个产生价值的不同客户_id.这消除了进一步"碰撞"的可能性.

  3. "进程ID".那么种子可以访问它(它应该是),那么生成的_id更多机会避免碰撞.

  4. "随机值".因此,另一个"客户端"以某种方式设法生成与上面相同的所有值,并且设法生成相同的随机值.

底线是,如果不是一个足够令人信服的消化论证,那么只需提供您自己的"uuid"条目作为"主键"值.

但恕我直言,这应该是一个公平的令人信服的论据,认为这里的碰撞方面是非常广泛的.至少可以说.

充满话题可能只是一点点"过于宽泛".但是我希望这会使人们更多地考虑"非常不可能"并且更加具体一些.