【问题标题】:How to generate 8 bytes unique id from GUID?如何从 GUID 生成 8 个字节的唯一 ID?
【发布时间】:2011-04-15 14:23:36
【问题描述】:

我尝试在我们的 C# 应用程序(不是全局的,仅用于一个会话)中为我们的事件使用 long 作为唯一 ID。不知道下面会不会生成一个唯一的long id?

public long GenerateId()
{
 byte[] buffer = Guid.NewGuid().ToByteArray();
 return BitConverter.ToInt64(buffer, 0);
}

为什么我们不直接使用 GUID?我们认为 8 字节长就足够了。

【问题讨论】:

标签: c# .net


【解决方案1】:

不,不会。正如 Raymond Chen 的博客上多次强调的那样,GUID 被设计为整体上是唯一的,如果你只剪掉它的一部分(例如,从它的 128 个字节中只取 64 个字节),它将失去其(伪)唯一性保证.


Here是:

一位客户需要生成一个 8 字节的唯一值,他们最初的想法是生成一个 GUID 并丢弃后半部分,保留前 8 个字节。他们想知道这是否是个好主意。

不,这不是一个好主意。 (...) 一旦您了解了它是如何工作的,很明显您不能只丢弃 GUID 的一部分,因为所有部分(除了固定部分)一起工作以建立唯一性。如果你拿走这三个部分中的任何一个,算法就会崩溃。特别是,只保留前八个字节(64 位)为您提供时间戳和四个常量位;换句话说,您所拥有的只是时间戳,而不是 GUID。

由于它只是一个时间戳,因此可能会发生冲突。如果两台计算机同时生成这些“截断的 GUID”之一,它们将生成相同的结果。或者,如果系统时钟由于时钟重置而在时间上倒退,您将开始重新生成您在该时间第一次生成的 GUID。


我尝试在我们的 C# 应用程序中使用 long 作为唯一 ID(不是全局的,并且仅用于一个会话。)我们的事件。你知道下面会生成一个唯一的long id吗?

你为什么不直接使用计数器?

【讨论】:

  • 我同意@Aliostad。 UUIDGUID 不会以任何方式保证唯一性......它只是非常不太可能是重复的。
  • @Aliostad, @Bobby:理论上正确,实践中无关紧要。使用本文中描述的算法,您需要两台具有相同 MAC 地址的机器同时生成具有相同时钟序列号的 GUID(理论上低至纳秒)。我会说这是非常非常不太可能:)。 GUID 被设计为独一无二的,并且被视为唯一,我很确定如果生成重复的 GUID,很多软件都会崩溃。
  • irrelevant in practice 与随机 Int64 位数相同。可能性是 2^64 中的 1...
  • @Aliostad:不适用于 Int64 生成的切割 GUID 的前 8 个字节。你只会得到一个时间戳,如果时钟分辨率不是很好,你可能会得到两个相同的 int64 用于后续事件。
  • Guid.NewGuid() 将提供 GUID 4,而不是 GUID 1。提供的链接仅考虑类型 1 GUID。类型 4 GUID 没有时间戳,但(除了版本字段)完全随机。
【解决方案2】:

您不能将 16 位值提炼成 8 位值,同时仍保持相同程度的唯一性。如果独特性很重要,不要“自己动手”任何东西。除非您真的知道自己在做什么,否则请坚持使用 GUID。

如果一个相对简单的唯一性实现就足够了,那么最好还是生成自己的 ID,而不是从 GUID 派生它们。以下代码 sn-p 是从我发现自己经常使用的“本地唯一标识符”类中提取的。它可以轻松定义字符输出的长度和范围。

using System.Security.Cryptography;
using System.Text;

public class LUID
{
    private static readonly RNGCryptoServiceProvider RandomGenerator = new RNGCryptoServiceProvider();
    private static readonly char[] ValidCharacters = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789".ToCharArray();
    public const int DefaultLength = 6;
    private static int counter = 0;

    public static string Generate(int length = DefaultLength)
    {
        var randomData = new byte[length];
        RandomGenerator.GetNonZeroBytes(randomData);

        var result = new StringBuilder(DefaultLength);
        foreach (var value in randomData)
        {
            counter = (counter + value) % (ValidCharacters.Length - 1);
            result.Append(ValidCharacters[counter]);
        }
        return result.ToString();
    }
}

在这种情况下,为了明确的人类可读输出,它排除了 1(一)、I(i)、0(零)和 O(o)。

要确定有效字符和 ID 长度的特定组合的“唯一性”有多有效,数学很简单,但拥有某种“代码证明”(Xunit)仍然很好:

    [Fact]
    public void Does_not_generate_collisions_within_reasonable_number_of_iterations()
    {
        var ids = new HashSet<string>();
        var minimumAcceptibleIterations = 10000;
        for (int i = 0; i < minimumAcceptibleIterations; i++)
        {
            var result = LUID.Generate();
            Assert.True(!ids.Contains(result), $"Collision on run {i} with ID '{result}'");
            ids.Add(result);
        }            
    }

【讨论】:

    【解决方案3】:

    不,不会。一个 GUID 有 128 位长度,一个长只有 64 位,您缺少 64 位信息,允许两个 GUID 生成相同的长表示。虽然机会很小,但它就在那里。

    【讨论】:

      【解决方案4】:

      根据Guid.NewGuid MSDN page

      新 Guid 的值全为零或等于任何其他 Guid 的可能性非常低。

      因此,您的方法可能产生一个唯一的 ID,但不能保证。

      【讨论】:

        【解决方案5】:

        是的,这将是最有可能唯一的,但由于位数少于 GUID,重复的可能性大于 GUID - 尽管仍然可以忽略不计

        无论如何,GUID 本身不保证唯一性。

        【讨论】:

        • 出于所有实际目的和目的,GUID 是唯一的。从数学上讲,你是对的,但是发生碰撞的可能性非常低,以至于没有实际意义。
        【解决方案6】:
        var s = Guid.NewGuid().ToString();
        var h1 = s.Substring(0, s.Length / 2).GetHashCode(); // first half of Guid
        var h2 = s.Substring(s.Length / 2).GetHashCode(); // second half of Guid
        var result = (uint) h1 | (ulong) h2 << 32; // unique 8-byte long
        var bytes = BitConverter.GetBytes(result);
        

        P. S. 非常好,伙计们,你们在这里和话题发起者聊天。但是需要其他用户的答案呢,比如我???

        【讨论】:

          【解决方案7】:

          就像其他一些人所说的那样,只参与 guid 是破坏其独特性的好方法。试试这样的:

          var bytes = new byte[8];
          using (var rng = new RNGCryptoServiceProvider())
          {
              rng.GetBytes(bytes);
          }
          
          Console.WriteLine(BitConverter.ToInt64(bytes, 0));
          

          【讨论】:

            【解决方案8】:

            根据当前时间戳(以秒为单位)生成一个 8 字节的 Ascii85 标识符。 保证每一秒都是独一无二的。在同一秒内生成 5 个 Id 的概率为 85%。

            private static readonly Random Random = new Random();
            public static string GenerateIdentifier()
            {
                var seconds = (int) DateTime.Now.Subtract(new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds;
                var timeBytes = BitConverter.GetBytes(seconds);
                var randomBytes = new byte[2];
                Random.NextBytes(randomBytes);
                var bytes = new byte[timeBytes.Length + randomBytes.Length];
                System.Buffer.BlockCopy(timeBytes, 0, bytes, 0, timeBytes.Length);
                System.Buffer.BlockCopy(randomBytes, 0, bytes, timeBytes.Length, randomBytes.Length);
                return Ascii85.Encode(bytes);
            }
            

            【讨论】:

              【解决方案9】:

              正如在大多数其他答案中已经说过的那样:不,您可以只使用 GUID 的一部分而不会失去唯一性。

              如果您需要更短且独特的东西,请阅读 Jeff Atwood 的这篇博文:
              Equipping our ASCII Armor

              他展示了如何在不丢失信息的情况下缩短 GUID 的多种方法。最短的是 20 字节(带有ASCII85 encoding)。

              是的,这比您想要的 8 个字节长得多,但它是一个“真正的”唯一 GUID……而所有将某些东西塞进 8 个字节的尝试很可能不会是真正唯一的。

              【讨论】:

                【解决方案10】:

                在大多数情况下,将两半按位异或就足够了

                【讨论】:

                • 请详细说明您的答案。
                • 随机比特序列的任何非空子集都是随机比特序列。两个随机序列之和是一个随机序列。随机序列和常数之和是一个随机序列。 XOR 是按位求和。将 GUID 的常数部分与非常数部分异或在一起将为我们提供最大可能熵的结果。
                【解决方案11】:

                这里的每个人都让这种方式变得比它需要的更复杂。这是一个糟糕的主意。

                GUID 1:AAAA-BBBB-CCCC-DDDD
                GUID 2:AAAA-BBBB-EEEE-FFFF

                扔掉每个 GUID 的后半部分,现在您有一个重复的标识符。 GUID 不能保证是唯一的,而且非常糟糕。您不应该依赖生成的内容的保证,并且不难解决这个问题。如果您需要对象、实体或其他任何东西的唯一标识符,让我们以数据库为例 - 这是最常见的,您应该生成一个 id,查看它是否已经存在,只有在不存在时才插入它。这在数据库中很快,因为大多数表都是基于 ID 索引的。 “最多。”如果您在内存或任何地方有某种小对象列表,您可能会将实体存储在某种哈希表中,您可以在其中查找它以查看生成的 GUID 是否已经存在。

                总而言之,取决于您的实际用例。数据库,首先找到 GUID,如果可能,重新生成,直到可以插入新项目。这真的只在关系数据库中很重要,它们不会自动为表中的项目生成 ID。 NoSQL DB 通常会生成一个唯一标识符。

                【讨论】:

                  猜你喜欢
                  • 2020-10-28
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-02-24
                  • 2013-08-24
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多