【问题标题】:What do you say of chopping type-4 UUID in this manner你怎么说以这种方式砍类型 4 UUID
【发布时间】:2010-12-16 04:08:10
【问题描述】:

检查一下,

    List<String> list = new ArrayList<String>();
    for (int i = 0; i < 10000; i++) {
        String value = (""+UUID.randomUUID().getLeastSignificantBits()).substring(3, 20);
        assertFalse(list.contains(value));
        assertTrue(value.length() < 18);
        list.add(value);
    }

这种方法就像魅力一样传递。我的印象是,取最低有效位比取最高有效位要好一些。因为在最高有效位中,您为某些信息固定了 6 位,而最低有效位并非如此。因此,平均而言,我们需要生成 2^29 个 UUID 才能与最高有效位发生冲突,但 2^32 个 UUID 与最低有效位发生冲突。参考:SO Thread我的假设正确吗?

现在,在这里,我将从该方法中获得的最低有效位中再砍掉 2 个最高有效位。我正在使用子字符串。请注意,我正在砍掉 2 个数字和一个符号位。 这不是说现在我们平均需要生成 2^31 个 UUID 才能发生碰撞吗?

确切地说,我正在尝试生成一个长度不应超过 17 位的唯一标识符。它必须是一个整数,而不是 Java 类型的意义。 我的方法有多可靠?

元信息:

实际上,我们正在与一些遗留系统集成,我们必须提供一些不超过 17 位的唯一编号。我假设他们将它作为数据库唯一键。在这种情况下,我们也可以使用序列,我首先提出了这一点。但是他们对我说,如果我能想出一个随机数就好了,这样消费者就猜不到了。

据我所知,关于 Java 中 UUID 的 type-4 实现,我们平均需要生成 2^61 个 UUID 才能发生冲突。这是否意味着我们需要生成 2^32 来获得最低有效位的冲突,以及 2^29 来获得最高有效位的冲突?如果是,那么假设我们需要平均生成 2^31 才能在切掉 2 个最左边的数字后在最低有效位上发生冲突,这是不正确的吗?

我也尝试使用 SecureRandom,但这也给了我 19 位数的长值。因此,我最终也先将其砍到数位。下面是它的代码。

    List<String> list = new ArrayList();
    Random random = new SecureRandom();
    for (int i = 0; i < 10000; i++) {
        String value = ""+random.nextLong().substring(2, 19);
        assertFalse(list.contains(value));
        assertTrue(value.length() < 18);
        list.add(value);
    }

我能想到的另一个选项是以“yyMMddHHmmssSSS+2-seq-digits”格式使用日期。但我想这将完全依赖于处理器,并且可以猜测。因为我不太确定在 99 轮之后我得到了毫秒的变化。也许我会,但这取决于处理器速度。不过,99 个同时请求是不太可能的。

【问题讨论】:

  • 您的实际问题是什么?
  • 好的,我要打问号了。对此感到抱歉。

标签: java uuid 64-bit unique-id


【解决方案1】:

我建议您使用 Random 或 SecureRandom 来生成随机位并将其转换为数字。那应该更便携。

我不明白你关于截断数字的观点。假设您正在从长周期 PRNG 的足够位中生成 17(十进制)数字,对于任何给定的生成数字对,您应该有 10**17 发生冲突的机会。如果来源很好,并且您使用了足够多的位,那么您“砍”就无关紧要了......

我不清楚10**17 中的 1 是否足够好。这取决于在任何给定时间将存在多少个数字(在您的持久存储中)。例如,如果您现有 4400 万个数字,则至少一对之间发生冲突的可能性约为 1%。

尝试将一些数字插入Birthday Paradox Calculator

编辑:我认为您需要的是一个生成器,它为您提供具有长周期长度的 64 位伪随机数,并且绝对保证不会重复您可能生成的更多数字。还必须能够保持生成器的状态并恢复它。然后,要获得一个 17 位十进制数字“随机”数,从生成器中获取下一个值并测试它是否在0 ... 10**17 - 1 范围内。如果是,请使用它,如果不重复。

如果您正确管理生成器,您将永远不会在系统的生命周期内重复出现,因此发生碰撞的风险为零。但重要的是使用 PRNG(不是真正的 RNG)并选择具有正确属性的 PRNG。

据我所知,Random 类提供了一个循环长度为2**48 的 PRNG;即你应该在数字开始重复之前获得2**48 数字(例如使用getLong() 方法)。 OTOH,SecureRandom 提供真正随机或伪随机,具有非常长的循环计数......但在每次调用中重复一个数字的机会很小但非零。

【讨论】:

  • 感谢您的解释。我认为任何这种算法都不太可能发生碰撞。但是还是有机会的。我正在考虑为此使用yyyyMMdd + db-seq(9)。这样我一天就限制了 999999999 个条目。或者yyMMddHHmmss + db-seq(5),表示一秒钟内限制99999个条目。两者对我来说都很好。谢谢。
  • 对于最后一种情况,BPC 告诉我,在发出 45 个号码后,您有 1% 的机会发生冲突;即,如果您的平均发行率为每秒 45 次,则在任何给定的秒内发生碰撞的可能性为 1%。
  • 您的意思是 SecureRandom long 并将其切碎以获得 17 位 long 值吗?
【解决方案2】:

好的,几个问题,我会尽力而为

  1. 如果你在低位有共谋但在高位没有,你仍然有唯一的 id。相反的情况也是如此。因此,您需要 2^61 个数字才能获得合谋。
  2. 以 0.5 的概率,您正在切掉 3 位数字,因为没有写入 + 号。因此,您总共有 2^41 个可能的数字,因此串通的样本量为 2^21。 (10^18 =~ 2^41)
  3. 让我们看看你是如何得到结果的:Random.getLong() 被调用了两次,然后你删除了一些位(PRNG 创建的随机位)。我看不出它比调用 Random.getLong() 或 getInt() 更可靠。

如果您需要 17 位数字,为什么不执行以下操作:

String id = String.valueOf(random.nextLong) % 1000000000000000000L);

请注意,它不会给出对称分布 - 由于 MAX_LONG 为 9223372036854775807L,因此 [0,23372036854775807] 范围内的数字出现的机会会稍好一些。

另外,你的方法和这个方法都不能保证唯一的 id。

【讨论】:

  • 谢谢朋友。好的,stackoverflow.com/questions/325443/generate-uuid-in-java。他说,如果我们不切任何东西并按原样获取最重要的位,我们必须生成 2^29 个 UUID 才能平均发生冲突。那是错的吗?如果不是最不重要的应该是 2^32。我对么?如果是的话,那么它是如何在仅仅砍掉 3 位数字后变成 2^16 的?
  • 我已经确定了答案 - 让我们将 10^18 作为所有 17 位数字的上限。它大约等于 2^41。
【解决方案3】:

UUID 算法是特定于实现的。

将 guid 分成更小的数字不会具有相同的唯一性传播或保证。你节省的比特币真的那么重要吗?

【讨论】:

  • 据我所知,关于 Java 中 UUID 的 type-4 实现,我们平均需要生成 2^61 个 UUID 才能发生冲突。这是否意味着我们需要生成 2^32 来获得最低有效位的冲突,以及 2^29 来获得最高有效位的冲突?如果是,那么假设我们需要平均生成 2^31 才能在切掉 2 个最左边的数字后在最低有效位上发生冲突是不正确的吗?
  • 实际上,我们正在与一些遗留系统集成,我们必须提供一些不超过 17 位的唯一编号。我假设他们将它作为数据库唯一键。在这种情况下,我们也可以使用序列,我首先提出了这一点。但他们对我说,如果我能想出一个随机数就好了,这样消费者就猜不到了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-08-18
  • 2018-12-19
  • 2021-10-18
  • 1970-01-01
  • 2021-11-15
  • 1970-01-01
  • 2021-04-19
相关资源
最近更新 更多