Подтвердить что ты не робот

Guid.NewGuid() VS генератор случайных строк из Random.Next()

Мой коллега и я обсуждаем, какой из этих методов следует использовать для автоматического создания идентификатора пользователя и идентификатора сообщения для идентификации в базе данных:

Один вариант использует один экземпляр Random и принимает некоторые полезные параметры, поэтому его можно повторно использовать для всех видов строковых (например, от четырехзначных цифровых контактов до 20-значных буквенно-цифровых идентификаторов). Здесь код:

// This is created once for the lifetime of the server instance
class RandomStringGenerator
{
    public const string ALPHANUMERIC_CAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
    public const string ALPHA_CAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    public const string NUMERIC = "1234567890";

    Random rand = new Random();
    public string GetRandomString(int length, params char[] chars)
    {
        string s = "";
        for (int i = 0; i < length; i++)
            s += chars[rand.Next() % chars.Length];

        return s;
    }
}

а другой вариант - просто:

Guid.NewGuid();

см. Guid.NewGuid в MSDN

Мы оба знаем, что Guid.NewGuid() будет работать для наших нужд, но я предпочел бы использовать собственный метод. Он делает то же самое, но с большим контролем.

Мой коллега полагает, что, поскольку пользовательский метод был приготовлен самостоятельно, он, скорее всего, создаст столкновения. Я признаю, что не знаю о реализации Random, но я предполагаю, что он так же случайен, как Guid.NewGuid(). Типичным использованием настраиваемого метода может быть:

RandomStringGenerator stringGen = new RandomStringGenerator();
string id = stringGen.GetRandomString(20, RandomStringGenerator.ALPHANUMERIC_CAPS.ToCharArray());

Изменить 1:

  • Мы используем Azure Tables, который не имеет функции автоматического увеличения (или аналогичного) для генерации ключей.
  • Некоторые ответы здесь просто говорят мне использовать NewGuid() ", потому что это то, что он сделал для". Я ищу более глубокую причину того, почему метод варки может с большей вероятностью генерировать коллизии при одинаковых степенях свободы, как Guid.

Изменить 2:

Мы также использовали метод cooked для генерации идентификатора сообщения, который, в отличие от токенов сеанса, должен выглядеть красиво для отображения в URL-адресе нашего веб-сайта (например http://mywebsite.com/14983336), поэтому здесь нет выбора, но конфликтов все равно следует избегать.

4b9b3361

Ответ 1

Я ищу более глубокую причину того, почему готовый метод может генерировать столкновения с той же степенью свободы, что и Guid.

Во-первых, как отметили другие, Random не является потокобезопасным; использование его из нескольких потоков может привести к повреждению его внутренних структур данных, так что он всегда создает одну и ту же последовательность.

Во-вторых, Random засевается на основе текущего времени. Два экземпляра Random созданные за одну и ту же миллисекунду (напомним, что миллисекунда - это несколько миллионов процессорных циклов на современном оборудовании), будут иметь одинаковое начальное число и, следовательно, будут создавать одинаковую последовательность.

В-третьих, я солгал. Random не засевается в зависимости от текущего времени; он засевается в зависимости от времени, в течение которого машина была активна. Начальное число - это 32-битное число, и, поскольку степень детализации составляет миллисекунды, то есть всего несколько недель, пока она не обернется. Но это не проблема; проблема в том, что период времени, в течение которого вы создаете этот экземпляр Random, с большой вероятностью может составлять несколько минут после загрузки машины. Каждый раз, когда вы выключаете или включаете новый компьютер в кластере, появляется небольшое окно, в котором создаются экземпляры Random, и чем больше это происходит, тем выше вероятность того, что вы получите начальное число. что у тебя было раньше.

(ОБНОВЛЕНИЕ: В более новых версиях .NET Framework некоторые из этих проблем были смягчены; в этих версиях у вас больше не создается каждый Random созданный за одну и ту же миллисекунду. Тем не менее, есть много проблем с Random, всегда помните, что это только псевдослучайный, а не криптостойкий случайный. Random на самом деле очень предсказуем, поэтому, если вы полагаетесь на непредсказуемость, он не подходит.)

Как уже говорили другие: если вам нужен первичный ключ для вашей базы данных, пусть база данных сгенерирует вам первичный ключ; пусть база данных сделает свою работу. Если вам нужен глобальный уникальный идентификатор, используйте guid; вот для чего они.

И, наконец, если вы заинтересованы в том, чтобы узнать больше об использовании и злоупотреблениях руководствами, вы можете прочитать мою серию руководств по руководству; часть первая здесь:

http://blogs.msdn.com/b/ericlippert/archive/2012/04/24/guid-guide-part-one.aspx

Ответ 2

Как написано в других ответах, у моей реализации было несколько серьезных проблем:

  • Безопасность потока: Случайный не является потокобезопасным.
  • Предсказуемость: метод не может использоваться для критических критически важных идентификаторов, таких как токены сеанса, из-за характера класса Random.
  • Столкновения:. Хотя метод создал 20 "случайных" чисел, вероятность столкновения не равна (number of possible chars)^20 из-за того, что начальное значение составляет только 31 бит и исходит из плохого источника. Учитывая одно и то же семя, любая длина последовательности будет одинаковой.

Guid.NewGuid() будет хорошо, за исключением того, что мы не хотим использовать уродливые GUID в URL-адресах и .NET. Новый алгоритм NewGuid() не известен как криптографически безопасный для использования в токенах сеанса - он может дать прогнозируемые результаты, если немного информация известна.

Вот код, который мы используем сейчас, он безопасный, гибкий, и насколько я знаю, очень маловероятно создавать конфликты при условии достаточной длины и выбора персонажа:

class RandomStringGenerator
{
    RNGCryptoServiceProvider rand = new RNGCryptoServiceProvider();
    public string GetRandomString(int length, params char[] chars)
    {
        string s = "";
        for (int i = 0; i < length; i++)
        {
            byte[] intBytes = new byte[4];
            rand.GetBytes(intBytes);
            uint randomInt = BitConverter.ToUInt32(intBytes, 0);
            s += chars[randomInt % chars.Length];
        }
        return s;
    }
}

Ответ 3

"Автоматическое создание идентификаторов пользователей и идентификаторов сообщений для идентификации в базе данных"... почему бы не использовать последовательность или идентификатор базы данных для генерации ключей?

На мой вопрос: "Каков наилучший способ создания первичного ключа в моей базе данных?" Если это так, вы должны использовать обычный инструмент базы данных, который будет либо последовательностью, либо идентичностью. Они имеют преимущества по генерируемым строкам.

  • Последовательности/индекс идентификации лучше. Существует множество статей и сообщений в блогах, которые объясняют, почему GUID и т.д. Делают плохие индексы.
  • Они гарантированно будут уникальными в таблице
  • Их можно безопасно сгенерировать с помощью параллельных вставок без столкновения.
  • Они просты в реализации

Я думаю, мой следующий вопрос: какие причины вы рассматриваете GUID или сгенерированные строки? Будете ли вы интегрироваться в распределенные базы данных? Если нет, вы должны спросить себя, разрешаете ли вы проблему, которая не существует.

Ответ 4

В вашем пользовательском методе есть две проблемы:

  • Он использует глобальный экземпляр Random, но не использует блокировку. = > Многопоточный доступ может испортить его состояние. После этого выход будет сосать даже больше, чем он уже делает.
  • Он использует предсказуемое 31-битное семя. Это имеет два последствия:
    • Вы не можете использовать его для чего-либо связанного с безопасностью, где важна важная проблема.
    • Маленькое семя (31 бит) может снизить качество ваших чисел. Например, если вы одновременно создаете несколько экземпляров Random (с момента запуска системы), они, вероятно, создадут ту же последовательность случайных чисел.

Это означает, что вы не можете полагаться на то, что вывод Random уникален, независимо от того, как долго он будет.

Я рекомендую использовать CSPRNG (RNGCryptoServiceProvider), даже если вам не нужна безопасность. Его производительность по-прежнему приемлема для большинства применений, и я бы доверял качеству своих случайных чисел над Random. Если вы хотите уникальности, я рекомендую получать номера с примерно 128 бит.

Чтобы генерировать случайные строки с помощью RNGCryptoServiceProvider, вы можете посмотреть мой ответ на Как я могу генерировать случайные 8 символов, буквенно-цифровые строки в С#?.


В настоящее время идентификаторы GUID, возвращаемые Guid.NewGuid(), являются GUID версии 4. Они генерируются из PRNG, поэтому у них есть довольно похожие свойства для генерации случайного 122-битного числа (остальные 6 бит фиксированы). Его источник энтропии имеет гораздо более высокое качество, чем использует Random, но он не гарантируется криптографической безопасностью.

Но алгоритм генерации может измениться в любое время, поэтому вы не можете полагаться на это. Например, в прошлом алгоритм генерации GUID Windows изменился с v1 (на основе метки времени MAC +) на v4 (случайный).

Ответ 5

Используйте System.Guid как это:

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

Обратите внимание, что Random является генератором псевдослучайных чисел. Это не случайно и не уникально. Он имеет только 32-разрядные значения для работы по сравнению с 128-битным GUID.

Однако даже GUID могут иметь коллизии (хотя шансы действительно тонкие), поэтому вы должны использовать собственные функции базы данных, чтобы дать вам уникальный идентификатор (например, столбец идентификатора автоинкремента). Кроме того, вы не можете легко превратить GUID в числовое число 4 или 20 (альфа).

Ответ 6

В отличие от того, что некоторые люди сказали в комментарии, GUID, сгенерированный Guid.NewGuid(), НЕ зависит от какого-либо идентификатора, определенного машиной (только GUID GUID типа 1, Guid.NewGuid() возвращает GUID типа 4, который в основном случайный).

Пока вам не нужна криптографическая защита, класс Random должен быть достаточно хорошим, но если вы хотите быть более безопасным, используйте System.Security.Cryptography.RandomNumberGenerator. Обратите внимание на то, что не все цифры в GUID являются случайными. Цитата из wikipedia:

В каноническом представлении xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx наиболее значимые биты N указывают вариант (в зависимости от варианта, используются один, два или три бита). Вариант, охватываемый спецификацией UUID, обозначается двумя наиболее значимыми битами N, равными 1 0 (т.е. Шестнадцатеричный N всегда будет 8, 9, A или B). В варианте, охватываемом спецификацией UUID, существует пять версий. Для этого варианта четыре бита M указывают версию UUID (т.е. Шестнадцатеричный M будет либо 1, 2, 3, 4, либо 5).

Ответ 7

Что касается вашего редактирования, вот одна из причин предпочесть GUID над сгенерированной строкой:

Собственное хранилище для GUID (uniqueidentifier) ​​в SQL Server составляет 16 байт. Чтобы сохранить эквивалентную длину varchar (string), где каждая "цифра" в id хранится как символ, требуется от 32 до 38 байт, в зависимости от форматирования.

Из-за его хранения SQL Server также может индексировать столбец uniqueidentifier более эффективно, чем столбцы varchar.