【问题标题】:How to generate absolutely unique GUID's?如何生成绝对唯一的 GUID?
【发布时间】:2011-11-02 13:32:43
【问题描述】:

有没有办法每次都生成一个 100% 新的 GUID 而不会在整个应用程序中发生冲突?

由于我无法在八小时内回答我的问题,我想出了解决方案:

internal static class GuidGenerator
{
    private static readonly HashSet<Guid> _guids = new HashSet<Guid>();

    internal static Guid GetOne()
    {
        Guid result;

        lock (_guids)
            while (!_guids.Add(result = Guid.NewGuid())) ;

        return result;
    }
    internal static void Utilize(Guid guid)
    {
        lock (_guids)
            _guids.Remove(guid);
    }
}

这段代码是否解决了应用内的问题?

编辑:呃,它变得复杂了。线程安全扼杀了速度。

【问题讨论】:

  • 您必须使用无限长的 GUID。
  • @AgentFire:GUID 是否应该是全球唯一的?对于整个应用程序,我相信.NewGuid() 就足够了。除非您是在谈论 universe 中的唯一 ID?
  • @AgentFire 这相当于在阳光明媚、万里无云的日子里待在室内以避免被闪电击中。
  • @AgentFire 只要您使用有限长度的 GUID,就永远不会 100% 保证您不会遇到 GUID 冲突。 Daniel 和 CodeInChaos 所说的是,在实践中实际发生冲突的可能性非常小,以至于您的应用程序由于其他原因而失败的可能性要大得多,例如运行它的硬件故障,而不是因为 GUID碰撞。
  • 你为什么不直接使用你递增的整数计数器?

标签: c#


【解决方案1】:

不,没有任何方法可以生成绝对唯一 GUID。只有 3.40282367 × 1038 个可能的 GUID,因此当星系发生碰撞时,这些标识符也会发生碰撞。即使对于单个应用程序,它也取决于应用程序有多少 GUID。除非您的应用程序比所有 Google 索引器的总和还要大,否则您无需为此失眠。只需使用Guid.NewGuid()

【讨论】:

  • 我想我们可以向 skeet 询问 Google 的索引器,一旦他回答了这个话题。
【解决方案2】:

当然。 GUID 只是一个 128 位的值。所以使用一个 128 位整数(例如,由两个 ulong 值表示)并递增它。当您达到 128 位整数类型的最大值时,您已生成所有可能的 GUID。例如:

public IEnumerable<Guid> GetAllGuids()
{
    unchecked
    {
        byte[] buffer = new byte[16];
        ulong x = 0UL;
        do
        {
           byte[] high = BitConverter.GetBytes(x);
           Array.Copy(high, 0, buffer, 0, 8);
           ulong y = 0UL;
           do
           {
               y++;
               byte[] low = BitConverter.GetBytes(y);
               Array.Copy(low, 0, buffer, 8, 8);
               yield return new Guid(buffer);
           } while (y != 0UL);
           x++;
        } while (x != 0UL);
    }
}

注意事项:

  • 这绝对没有想象中的那么高效。
  • 遍历所有可能的 ulong 值是一件痛苦的事 - 我不喜欢使用 do...while...
  • 如 cmets 中所述,这将产生无效的值 UUIDs

当然,这绝不是随机的……

实际上,正如其他人所提到的,Guid.NewGuid 发生冲突的可能性非常小。

【讨论】:

  • @Sangram:我不确定你想表达什么观点。使用正常方法发生碰撞的可能性很小但非零。我的代码生成所有可能的 GUID,没有任何重复。
  • @AgentFire:这比使用 do/while 版本的 IMO 糟糕得多。
  • @AgentFire:是的,我更喜欢这种方式。我个人不喜欢在更大的表达式中使用前缀/后缀增量 - do/while 对我来说比这更清楚。
  • @AgentFire:不,实际上我没有。尝试写完整的东西,你可能会明白我为什么不使用它。提示:如果你使用y &lt; ulong.MaxValue,你将永远不会看到y == ulong.MaxValue...(在循环的开头没有y的值,这意味着你应该终止。)
  • 代码有bug。第二个 Array.Copy 应该是 Array.Copy(low, 0, buffer, 8, 8);正如所写的那样,代码将继续生成 0 GUID。
【解决方案3】:

不是 100%。但如果您的 GUID 生成器运行良好,那么碰撞概率非常非常小。这实际上可以算作 0。

随机生成的(类型 4)guid 大约有 120 个随机位。从生日问题可以看出,一旦生成大约 2^60 或 10^18 个 GUID,就很可能发生冲突,这该死的很多。

所以简单地使用Guid.NewGuid() 就足够了。


IMO 您提出的解决方案不是一个好主意:

  • 如果您有很多 GUID,可能会占用大量内存
  • 由于您需要了解本地的所有 GUID,因此没有理由首先使用 GUID。一个简单的整数计数器也可以完成这项工作。
  • 随机 GUID 冲突的可能性低于损坏数据结构的故障硬件。

您的代码本身在我看来是正确的。即,如果您注册了所有 GUID,并且您的硬件运行良好,并且软件没有其他错误,则保证不会发生冲突。

当然它也不是线程安全的,这对于静态方法来说是意料之外的。

【讨论】:

    【解决方案4】:

    如果您使用有限数量的字符,那么根据Pigeonhole(也称为Dirichlet)原则,您总是有机会收到冲突。

    【讨论】:

      【解决方案5】:
      var newGuid = Guid.NewGuid();
      

      http://msdn.microsoft.com/en-us/library/system.guid.newguid.aspx

      编辑 - 我同意@David Heffernan 所说的。您可以使用现有机制来生成最佳唯一标识符,但在这个宇宙中,您可以 100% 依赖的东西很少。

      【讨论】:

      • 喜欢Bobby Tables 滥用你的数据库:P
      【解决方案6】:

      如果您在上下文中需要唯一的 GUID,只需从 00000000-0000-0000-0000-000000000000 开始并使用增量。所有生成的 GUID 都是唯一的,除非您达到 FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF

      【讨论】:

        【解决方案7】:

        这取决于你想要什么。如果您希望生成的 GUID 具有唯一性,则可以实现。只需维护一个 GUID 列表,当您需要创建一个新的 GUID 时,这样做是一个循环,直到您找到一个不在您的列表中。

        如果您想要某种全球唯一性,即全球意味着在整个星球上使用的所有 GUID 中,那么这永远无法实现。

        【讨论】:

          【解决方案8】:

          您可以使用Guid.NewGuid()。它会为您生成 GUID,我相信您不会与另一个 GUID 发生冲突。

          【讨论】:

            【解决方案9】:

            除了少量的 GUID 之外,存储当前的 GUID 是不切实际的 - 无论如何,这将有极低的碰撞机会。

            在您定期生成可能数百万甚至数十亿个 GUID 的实际场景中,存储 128 位值以确保唯一 GUID 的开销变得不切实际。 (每个 GUID 16 个字节)

            对于仅仅 10,000,000,000 个 GUID,您需要 160,000,000,000 字节 = 156,250,000 KB = 152,588 MB = 149 GB

            大表中的查找时间也会使生成新的唯一 GUID 慢到虚拟爬网(在 CPU 时间范围内),特别是当新的 GUID 与现有值发生冲突时会导致生成新的 GUID - 然后需要检查,等等

            生成一个“随机”的 128 位值,甚至使用(当前时间 * 处理器时钟)之类的值可能“足够接近”——仅 45 位就足以存储 1000 年的毫秒计数。 128 位为您提供 9,671,406,556,917,033,397,649,408 倍的值。

            无论您做什么,128 位值都会发生冲突,即使使用计数器也是如此。

            【讨论】:

              【解决方案10】:

              Guid.NewGuid() 是生成不会与另一个 GUID 冲突的 GUID 的最不可能的方法。除非您生成 GUID 并查看现有 GUID 以确保它们不存在,否则无法 100% 确定。

              【讨论】:

              • 您的意思是Guid.NewGuid 很可能会导致冲突,还是很可能不会导致冲突?我很困惑。
              • 很可能不是。这是最不可能引起冲突的方法。
              【解决方案11】:

              GUID http://msdn.microsoft.com/en-us/library/system.guid.newguid.aspx

              编辑 - 我同意@David Heffernan 所说的。您可以使用现有机制来生成最佳唯一标识符,但在这个宇宙中,您可以 100% 依赖的东西很少。

              【讨论】:

              • 你的答案一半只是一个链接(没有任何解释),另一半是你对其他答案的看法。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-08-30
              • 2017-07-05
              • 2010-12-25
              • 1970-01-01
              相关资源
              最近更新 更多