生成的UUID随机重复了。

14

我正在使用以下函数生成 UUID:

UUID.randomUUID().toString()

我们的生产环境有50多个服务器(应用服务器 - 每个都是自己的JVM),对于落在这些服务器上的请求,作为第一步,我们会生成一个UUID,它基本上唯一标识了一个事务。

我们观察到,在第6和第11台服务器上,每天至少有10到15条消息的UUID匹配,这很奇怪,因为考虑到负载即每天约100万个事务,这些UUID在同一天内重复非常不寻常。

我们迄今为止所做的:

  1. 验证应用程序日志 - 我们没有发现任何可疑的内容,所有日志都正常
  2. 尝试在具有类似生产负载和50多个服务器的测试环境中复制此问题 - 但在测试环境中并未发生此问题
  3. 检查应用程序逻辑 - 这似乎不是问题,因为除了有相同代码库副本的6和11以外,其他48个服务器都运行得非常正常,并且它们为每个事务生成唯一的UUID。

到目前为止,我们还没有能够跟踪到这个问题,我的问题基本上是,如果有什么我们在JVM级别上错过的东西,或者我们需要设置UUID参数来解决这种一次性的问题吗?


1
这可能是无用的建议,但如果您找到了一种生成基于MAC +时间戳的V1而不是V4的方法,它可能会减少冲突,因为它们必须在同一时间、同一台机器上发生,并且非常不幸。 - JCOC611
10
作为第一步,我会将每台机器上 UUID.randomUUID() 生成的每个 UUID 记录在本地文本文件中。然后,我会在这些日志上重新运行查找重复 UUID 的操作。可能是因为在您实际的代码中,UUIDs 在后续阶段混淆了,例如由于更高层次的某个位置存在竞争条件等原因。 - NPE
2
我也会搜索您的整个代码库,看看是否有任何地方可能正在初始化随机数生成器。;-) - NPE
2
你在多个位置生成伪随机UUID。如果你没有发现其他错误,考虑要么在一个位置生成所有的伪随机UUID,要么生成真正的随机UUID。 - mattm
3
在正常操作下,由虚拟机生成的随机数不应该呈现出这种碰撞。但是,假设你用虚拟机 A 生成了一组随机数,然后对 A 进行了快照。然后过了一段时间,停止了 A,从快照中恢复,然后继续生成随机数 - 由于重用内部状态值,你可能会得到一些重复的数字。我怀疑如果你从快照开始 A 和 B,那么你可能会遇到同样的问题。 - iheanyi
显示剩余3条评论
4个回答

6
给你时间,我相信你会找到罪犯。与此同时,有一条评论我认为应该被提升为答案:

您在多个位置生成伪随机UUID。如果您没有发现其他错误,请考虑在一个位置生成所有伪随机UUID,或者生成真正的随机UUID。

因此,创建一个UUID服务器。它只是一个生成UUID块的进程。每个块可能包含10,000(或适当数量)个UUID。该进程会在验证块不包含重复项后将每个块写入磁盘。

创建另一个进程来分发UUID块。也许它只是一个Web服务,当它收到请求时返回未使用的块。交易服务器请求一个块,然后在创建交易时使用这些UUID。当服务器使用了大部分分配的UUID时,它会请求另一个块。

0

我不会浪费时间去想 UUID.randomUUID() 如何每天生成一些重复的 UUID。这种情况发生的概率微乎其微。(如果底层 RNG 状态被复制,那么生成整个系列的重复是可能的,但这似乎并非如此。)

相反,应该寻找一个服务器存储的 UUID 可能会覆盖另一个服务器存储的 UUID 的地方。为什么只有在 50 台服务器中的两台之间才会发生这种情况?这与您的环境和系统的细节有关,这些细节尚未被分享。


0

您的数据库列长度可能会限制您经历重复的 UUID


0
如上所述,合法碰撞的机会非常小。更可能的情况是,如果值以不正确的方式在对象之间传输,则会发生问题。
对于像Java这样按引用传递的语言,请考虑以下场景。
saveObject1.setUUID(initObj.getUUID())
initObj.setUUID(UUID.randomUUID());
saveObject2.setUUID(initObj.getUUID()) 

在这种情况下,saveObject1和saveObject2将具有相同的值,因为它们都指向同一个对象引用(initObj的UUID引用)。

像这样的问题似乎比实际的UUID冲突更有可能发生,特别是如果您可以重现它。如果它不是一直发生,那么很可能是更复杂的问题,例如罕见的竞态条件,其中initObj没有及时重新初始化,导致saveObject1和2共享同一个对象引用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接