Как создать уникальный идентификатор 8 байт из GUID?

Я пытаюсь использовать long как уникальный идентификатор в нашем приложении c# (не глобальный, а только для одного сеанса) для наших событий. Вы знаете, будет ли следующее генерировать уникальный длинный идентификатор?

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

почему мы не используем GUID напрямую? Мы думаем, что 8 байт достаточно хороша.

9 ответов


нет, не будет. Как подчеркивалось много раз в блоге Раймонда Чена, GUID разработан, чтобы быть уникальным в целом, если вы вырежете только часть его (например, взяв только 64 байта из 128), он потеряет свои (псевдо-)гарантии уникальности.


здесь это:

клиенту нужно было создать 8-байтовое уникальное значение, и их первоначальная идея состояла в том, чтобы создать GUID и выбросить вторую половину, сохранив первые восемь байтов. Они хотел узнать, хорошая ли это идея.

нет, это плохая идея. (...) Как только вы увидите, как все это работает, ясно, что вы не можете просто выбросить часть GUID, так как все части (ну, за исключением фиксированных частей) работают вместе, чтобы установить уникальность. Если вы уберете любую из трех частей, алгоритм развалится. В частности, сохранение только первых восьми байтов (64 бита) дает вам метку времени и четыре постоянных бита; другими словами, все, что у вас есть, это временная метка, а не GUID.

поскольку это просто временная метка, у вас могут быть столкновения. Если два компьютера генерируют один из этих "усеченных GUID" одновременно, они будут генерировать тот же результат. Или если системные часы идут назад во времени из-за сброса часов, вы начнете регенерировать GUID, которые вы создали в первый раз, когда это было в тот раз.


Я пытаюсь использовать long как уникальный идентификатор в нашем приложении c# (не глобальный, и только на один сеанс.) для наших мероприятий. знаете ли вы, что следующее создаст уникальный long id?

Почему бы вам просто не использовать счетчик?


на Guid.Метод newguid MSDN в разделе,

вероятность того, что значение нового Guid будет все нули или равно любому другому Guid, очень мала.

Итак, ваш способ мая создайте уникальный идентификатор, но это не гарантируется.


вы не можете дистиллировать 16-битное значение до 8-битного значения, сохраняя при этом ту же степень уникальности. Если уникальность имеет решающее значение, не" катите свой собственный " ничего. Придерживайтесь GUIDs, если вы действительно не знаете, что делаете.

Если достаточно относительно наивной реализации уникальности, все равно лучше генерировать свои собственные идентификаторы, а не выводить их из 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) для однозначного читаемого человеком вывода.

чтобы определить, насколько эффективно "уникальна" ваша конкретная комбинация допустимых символов и длины идентификатора, математика достаточно проста, но все равно приятно иметь своего рода "доказательство кода" (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);
        }            
    }

нет, не будет. GUID имеет длину 128 бит, длинный только 64 бит, вам не хватает 64 бит информации, что позволяет двум GUID генерировать одно и то же длинное представление. Хотя шанс очень мал, он есть.


Да, это будет скорее всего уникальный, но поскольку количество бит меньше 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 на основе текущей метки времени в секундах. Гарантированный уникальный для каждой секунды. 85% вероятность отсутствия столкновений для 5 сгенерированных идентификаторов в течение той же секунды.

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, не теряя уникальности.

Если вам нужно что-то более короткое и уникальное, прочитайте это сообщение в блоге Джеффа Этвуда:
оснащение нашей 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);

P. S. Это очень хорошо, ребята, что вы общаетесь с топик-стартера здесь. Но как насчет ответов, которые нужны другим пользователям, таким как я???