2013-02-20 3 views
24

Моего коллеги и я обсуждаем, какие из этих методов использовать для автоматических генерирующего пользователя идентификаторов и пост ID для идентификации в базе данных:Guid.NewGuid() VS случайная строка генератор от Random.Next()

One опция использует один экземпляр Random и принимает некоторые полезные параметры, поэтому его можно повторно использовать для всех видов строковых (например, от 4-значных цифровых контактов до 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 on MSDN

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

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

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

Edit 1:

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

Edit 2:

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

+9

'Random' делает * НЕТ * гарантия уникальности. Совершенно верно, что случайная последовательность содержит один и тот же результат несколько раз. – GalacticCowboy

+0

Если вы готовите его самостоятельно, он вряд ли будет ** UNIQUE **, а затем GUID. GUID использует определенные аппаратные факторы и текущее время для генерации определенного GUID. Вы можете самостоятельно исследовать детали. GUID не будет уникальным, если вы слишком часто удаляете один и тот же GUID-генератор за короткий промежуток времени. Опять же, вы можете сами посмотреть детали. – StarPilot

+1

Если вы хотите генерировать уникальные идентификаторы пользователей и почтовые идентификаторы, вам следует использовать автоматически увеличивающиеся числа из базы данных. Удалите базу данных, верните следующий номер в этой последовательности. Гарантировано уникальное. – StarPilot

ответ

45

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

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

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

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

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

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

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

+1

Спасибо за ваш ответ, руководство руководство было полезно и ваши спекуляции в [часть 3] (http://blogs.msdn.com/b/ericlippert/archive/2012/05/07/guid-guide-part-three. aspx) заставляют нас генерировать свои собственные идентификаторы и сеансовые токены, используя 'RNGCryptoServiceProvider', а не' NewGuid() '. См. Мой собственный ответ на этот вопрос ниже. –

+12

+1 за ложь, а затем признался, что ты солгал. – Alan

1

Использование System.Guid как это:

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

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

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

+2

Быть псевдослучайным не является проблемой как таковой (v4 GUID - это просто псевдослучайные числа), но «Random» использует очень плохое семя. – CodesInChaos

4

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

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

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

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

+0

Как вы заметили, GUID кажутся наиболее полезными в сценариях распределенного или отключенного редактирования. В противном случае вы в основном делаете больше работы для себя без какой-либо реальной выгоды. – GalacticCowboy

+1

Я не вижу 2, 3 и 4 как большие преимущества по сравнению с GUID или вручную генерируемыми случайными строками (используя хороший PRNG). Вероятность столкновения незначительна. Точка 1 является большим недостатком случайных идентификаторов. Даже с распределенными системами «машинный идентификатор + счетчик» часто предпочтительнее. – CodesInChaos

+0

Мы используем таблицы Azure и обращаемся к ним из лазурного рулона. Я не эксперт базы данных, но я думаю, что GUID/случайные строки - единственный вариант здесь? –

1

В отличие от некоторых людей, высказанных в комментарии, 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).

3

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

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

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

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

Для генерации случайных строк с использованием RNGCryptoServiceProvider вы можете взглянуть на мой ответ на How can I generate random 8 character, alphanumeric strings in C#?.


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

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

0

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

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

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

+0

Редактирование говорит, что я не использую SQL или какие-либо его функции GUID. Я использую azure таблицы, которым нужна строка в виде строки. –

+0

«Он поддерживает уникальный идентификатор», вы говорите о таблицах SQL azure или Azure? Я не использую SQL-лазурь, и я не уверен, что вы подразумеваете под таблицами Azure. ** edit **: вы удалили свой комментарий ... –

+0

Да, в дальнейшем я решил, что мой комментарий (и фактически весь этот ответ) действительно не применим к вашей ситуации. В общем случае вопрос * title * и * details * действительно о двух разных вещах. – GalacticCowboy

7

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

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

Guid.NewGuid() было бы хорошо, за исключением того, что мы не хотим использовать уродливые GUIDs в URLs и .NETs 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; 
    } 
} 
+0

Просто любопытно: не существует ли небольшое предпочтение некоторых элементов в 'chars', когда' uint.MaxValue' не кратно 'chars.Length', потому что вы используете здесь оператор modulo? Не уверен, что это актуально. – ygoe

Смежные вопросы