如何从GUID生成8个字节的唯一ID?

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

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

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

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


这是:

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

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

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


我尝试在我们的C#应用​​程序中使用long作为唯一ID(不是全局的,仅用于一个会话。)用于我们的事件。 你知道以下内容会产生一个独特的长ID吗?

你为什么不用柜台?

根据Guid.NewGuid MSDN页面 ,

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

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

您不能将16位值提取到8位值,同时仍保持相同的唯一性。 如果唯一性至关重要,请不要“自己动手”。 坚持使用GUID,除非你真的知道你在做什么。

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

 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长度的特定组合的“独特”有多么有效,数学很简单,但是对于各种类型的“代码certificate”(Xunit)仍然很好:

  [Fact] public void Does_not_generate_collisions_within_reasonable_number_of_iterations() { var ids = new HashSet(); 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); } } 

不,它不会。 GUID具有128位长度,长度仅为64位,您缺少64位信息,允许两个GUID生成相同的长表示。 虽然机会很小,但它确实存在。

是的,这很可能是唯一的,但由于比特数小于GUID,重复的可能性大于GUID – 尽管仍然可以忽略不计

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

像其他一些人所说的那样,仅仅采取部分指导是破坏其独特性的好方法。 尝试这样的事情:

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

根据当前时间戳(以秒为单位)加入一个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); } 

正如大多数其他答案中已经说过的那样:不,你不能只是在不失去唯一性的情况下参与GUID的一部分。

如果您需要更短且仍然独特的东西,请阅读Jeff Atwood撰写的这篇博客文章:
配备我们的ASCII装甲

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

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

 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); 

PS这是非常好的,伙计们,你在这里与主题入门者聊天。 但是那些需要其他用户的答案呢,比如我???