Использование GUID в качестве первичного ключа


32

Я обычно использую идентификаторы автоинкремента как первичные ключи в базах данных. Я пытаюсь узнать о преимуществах использования GUID. Я прочитал эту статью: https://betterexplained.com/articles/the-quick-guide-to-guids/

Я понимаю, что эти GUID используются для идентификации объектов на уровне приложения. Они также хранятся в качестве первичного ключа на уровне базы данных. Например, скажем, у меня был следующий класс:

public class Person
{
public GUID ID;
public string Name;
..

//Person Methods follow
}

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

Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

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

Я читал эту статью ранее: http://enterprisecraftsmanship.com/2014/11/15/cqs-with-database-generated-ids/ . Меня это немного смущает, так как кажется, что я рекомендую счастливую среду между GUID и целыми числами в качестве первичных ключей.

Изменить 06/06/18

Я пришел к выводу, что направляющие больше подходят для моих требований, чем целые. Я использую CQRS больше в эти дни, и GUID лучше вписываются.

Я заметил, что некоторые разработчики моделируют GUID как строки в модели домена, например, здесь: https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/ Buyer.cs - в данном случае: IdentityGuid - это GUID, смоделированный как строка. Есть ли причина делать это иначе, чем указано здесь: Использовать объект пользовательского значения или Guid в качестве идентификатора объекта в распределенной системе? , Является ли "нормальным" моделирование GUID в виде строки или я должен моделировать его как GUID в модели и базе данных?



7
Не гарантированно быть уникальным, хотя вряд ли вы когда-нибудь увидите столкновение. stackoverflow.com/questions/1155008/how-unique-is-uuid/...
icirellik

2
Смотрите также: UUID столкновений
комар

2
См. Также dba.stackexchange.com/questions/54690/… , а также множество других вопросов - эта тема часто задавалась, отвечала и обсуждалась.
Гринстоун Уолкер

1
Система, с которой я сейчас работаю, использует UUID. Приятным свойством является то, что идентификатор однозначно идентифицирует запись, а не последовательный идентификатор, который идентифицирует запись в этой таблице.
Джастин

Ответы:


41

GUID по определению являются «глобально уникальными идентификаторами». В Java есть похожая, но немного другая концепция, называемая UUID «Универсально уникальные идентификаторы». Имена являются взаимозаменяемыми для любого практического использования.

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

Некоторые факты про-GUID:

  • GUID предотвращают столкновения ключей
  • GUID помогают объединять данные между сетями, машинами и т. Д.
  • SQL Server имеет поддержку полупоследовательных GUIDS, чтобы помочь минимизировать фрагментацию индекса ( см. Некоторые предупреждения)

Некоторое уродство с GUID

  • Они большие, по 16 байт каждый
  • Они вышли из строя, поэтому вы не можете сортировать по идентификатору и надеетесь получить порядок вставки, как вы можете по идентификаторам автоинкремента
  • С ними труднее работать, особенно с небольшими наборами данных (например, с поиском таблиц)
  • Новая реализация GUID является более надежной на SQL Server, чем в библиотеке C # (вы можете использовать последовательные GUID из SQL Server, в C # она случайная)

GUID увеличит ваши индексы, поэтому стоимость дискового пространства при индексации столбца будет выше. Случайные GUID фрагментируют ваши индексы.

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

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


18
Насколько я понимаю, GUID являются синонимами UUID. UUID - это стандартное имя. GUID - это то, что Microsoft разработала для них до RFC 4122 .
JimmyJames

13
«Они вышли из строя, поэтому вы не можете сортировать по идентификатору и надеяться получить порядок вставки, как вы можете использовать для идентификаторов с автоинкрементом» Честно говоря, мне неудобно полагаться и на обычные идентификаторы. Несмотря на то, что в крайнем крайнем случае более низкий идентификатор может быть зафиксирован на диске позже, я бы предпочел использовать полезные данные сортировки, такие как метка времени вставки. Идентификаторы должны рассматриваться как адреса памяти - все есть, но само значение не имеет смысла. Максимально используйте их для тай-брейков. Тем более, что если у вас есть большая загрузка, порядок вставки не гарантируется.
Заводная муза

8
@CortAmmon Согласно Википедии и RFC 4122 , они являются синонимами. П. Лич из Microsoft был одним из создателей RFC. Я думаю, что с тех пор, как RFC был создан, они одинаковы. Из RFC: «UUIDs (универсальный уникальный IDentifier), также известный как GUID (глобально уникальный IDentifier)». Я думаю также полезно отметить, что GUID не были созданы MS. Они просто создали новое имя для технологии, принятой из других источников.
JimmyJames

6
«SQL Server оптимизирован для работы с GUID, поэтому он не должен сильно влиять на производительность запросов». -1 Не достаточно оптимизирован. Я работаю с БД, где все ПК являются направляющими, и это одна из основных причин низкой производительности.
Энди

7
«SQL Server оптимизирован для работы с GUID, поэтому он не должен сильно влиять на производительность запросов». Не соответствует действительности. Это утверждение предполагает, что другие типы данных не оптимизированы. Например, серверы баз данных имеют оптимизацию для работы с простыми значениями int. GUID / UUID намного медленнее, чем использование 4-байтового значения int. 16 байтов никогда не будут такими же быстрыми, как 4 байта - особенно на машине, которая обрабатывает не более 4 или 8 байтов изначально.
Эндрю Хенле

28

Это всегда будет уникальным?

Всегда? нет не всегда; это конечная последовательность битов.

Скажем, у меня была база данных, содержащая миллионы и миллионы строк с GUID в качестве первичного ключа.

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

Могу я просто сделать это?

Вы можете; это не совсем хорошая идея. Ваша модель домена обычно не должна генерировать случайные числа; они должны быть входными данными для вашей модели.

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

Смотрите описание имени на основе uuid в RFC 4122

Является ли "нормальным" моделирование GUID в виде строки или я должен моделировать его как GUID в модели и базе данных?

Я не думаю, что это имеет большое значение. Для большей части вашей доменной модели это идентификатор ; единственный вопрос, который вы спрашиваете, это то же самое, что и какой-либо другой идентификатор. Ваша модель домена обычно не будет смотреть на представление идентификатора в памяти.

Если GUID доступен как «примитивный тип» в настройке вашего домена, я бы использовал его; это позволяет вспомогательному контексту выбирать подходящие оптимизации, которые могут быть доступны.

Однако следует признать, что представление идентификатора, как в памяти, так и в хранилище, является решением, которое вы принимаете в своей реализации, и, следовательно, вы должны предпринимать шаги для обеспечения того, чтобы след кода был связан с этим. решение невелико - см. Parnas 1972 .


20
+1 за "вы уже исчерпали место на диске к тому времени, когда это происходит".
w0051977

2
Я чувствую, что концепция « детерминированного UUID » имеет важное значение (см. Data Vault 2)
алк

Действительно, возможность пересчитать UUID / GUID на основе других данных является огромной помощью, особенно для обнаружения дубликатов. Однажды я построил систему обработки сообщений, которая сохраняла сообщения и проталкивала их через конвейер обработки. Я создал хеш сообщения и использовал его в качестве первичного ключа во всей системе. только это само по себе решило мне МНОГИЕ проблемы, чтобы идентифицировать сообщение, когда мы должны были масштабироваться.
Newtopian

Миллион миллионов = 2 ^ 40. Это составляет 2 79 пар возможных столкновений. GUID имеет 2 ^ 128 битов, поэтому вероятность составляет один из 2 ^ 49. Гораздо более вероятно, что у вас есть ошибка, которая использует один и тот же GUID для двух записей, или ошибочно полагает, что существует коллизия там, где ее нет.
gnasher729

Я возвращаюсь к своим историческим вопросам. Прежде чем я приму; не могли бы вы взглянуть на мое редактирование?
w0051977

11

GUID или UUID , скорее всего, будут уникальными из-за того, как они генерируются, и они обеспечивают безопасный способ гарантировать уникальность без связи с центральным органом.

Преимущества GUID в качестве первичного ключа:

  • Вы можете копировать данные между различными сегментами кластера, и вам не нужно беспокоиться о коллизиях PK.
  • Это позволяет вам знать ваш первичный ключ до того, как вы вставите какие-либо записи.
  • Упрощает логику транзакции для вставки дочерних записей.
  • Не может быть легко угадать.

В приведенном вами примере:

Person p1 = new Person();
p1.ID = GUID.NewGUID();
PersonRepository.Insert(p1);

Указание GUID до времени вставки может сохранить двустороннюю передачу в базу данных при вставке последовательных дочерних записей и позволит вам зафиксировать их в одной и той же транзакции.

Person p2 = new Person();
p2.ParentID = p1.ID
PersonRepository.Insert(p2);

Ущерб GUID в качестве первичного ключа:

  • Это большие 16 байтов, что означает, что они будут занимать больше места по мере добавления индексов и внешних ключей.
  • Они плохо сортируются, так как они по сути случайные числа.
  • Использование индекса очень, очень, очень плохо.
  • Много листьев движется.
  • Их трудно запомнить.
  • Их трудно выразить словами.
  • Они могут сделать URL труднее для чтения.

Если ваше приложение не нуждается в сегментировании или кластеризации, было бы лучше придерживаться меньших, более простых типов данных, таких как int или bigint.

Многие базы данных имеют свои собственные внутренние реализации, которые пытаются смягчить проблемы с хранилищем, вызванные GUID, и SQL Server даже имеет функцию newsequentialid, помогающую упорядочить UUID, позволяющую лучше использовать индексы, и они, как правило, имеют лучшие характеристики производительности.

Кроме того, с точки зрения тестера, пользователя или разработчика, работающего с приложением, использование идентификатора через GUID значительно улучшит связь. Представьте, что вам нужно прочитать GUID по телефону.

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


1
Следует учитывать, что в зависимости от типа UUID они содержат информацию, которая потенциально может использоваться для идентификации машины, на которой они генерируются. Чисто случайный вариант может столкнуться с большей вероятностью без достаточной энтропии. Это следует учитывать перед использованием в URI.
JimmyJames

Согласен, хотя никогда не следует выставлять их первичный ключ в URL. Следует использовать более подходящий метод, чтобы гарантировать отсутствие утечки данных во внешнюю систему.
icirellik

1
Есть еще один вариант использования: тяжелые базы данных OLTP, в которых блокировка последовательности является узким местом. По словам моего друга Oracle DBA, это не так редко, как кажется, вам даже не нужны крупные или кластеры для этого. • В конце взвесите все за и против (и не путайте плюсы и минусы UUID с плюсами / минусами, которые не являются специфическими для UUID, как это делают некоторые авторы), и измерьте .
17

1
Если вы используете newsequentialid, то вам нужно пойти в базу данных, чтобы получить идентификатор (как с идентификацией int), не так ли? В чем тут выгода.
w0051977

1
@mirabilos Чтобы быть ясным, когда я говорю ужасно, у нас заканчивались вставки, которые занимали минуты на строку. Все началось хорошо, но после десятков тысяч рядов, он пошел очень быстро. Если это не очевидно, десятки тысяч строк - это очень маленькая таблица.
JimmyJames

4

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

Дополнительные 12 байтов суммируются быстро; помните, что большинство PK будут FK в других таблицах, и только три FK в таблице теперь имеют дополнительно 48 байтов для каждой строки. Это складывается в таблице и в индексах. Это также добавляет в дисковый ввод-вывод. Эти дополнительные 12 байтов должны быть прочитаны и записаны.

И если вы не используете последовательные направляющие, а PK кластеризованы (что происходит по умолчанию), SQL время от времени придется перемещать целые страницы данных, чтобы втиснуть больше в нужное «место». Для базы данных с высокой степенью транзакций, с большим количеством вставок, обновлений и удалений, все идет быстро.

Если вам нужен какой-то уникальный идентификатор для синхронизации или что-то еще, добавьте столбец guid. Только не делай это ПК.


4
Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

Это, безусловно, самая важная причина для использования GUID.

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

Вы можете быть уверены, что объект Person, который вы только что сгенерировали на своем сервере, ПК, ноутбуке, автономном устройстве или что-то еще, уникален для всех ваших серверов по всему миру, как бы он ни был распределен.

Вы можете вставить его в любую базу данных rdb или no-sql, файл, отправить его в любой веб-сервис или сразу же выбросить как ненужное

Нет, вы никогда не столкнетесь.

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

Да, это больше, чем int.

  • редактировать. пришлось отстрелять перед тем как закончить.

Я знаю, что многие люди испытывают сильные чувства к автоинсталляциям, и это спорная тема с администраторами

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

У авто инков есть много недостатков

  • Вы используете распределенную базу данных No-Sql. Вы просто не можете поговорить со всеми другими экземплярами, чтобы узнать, каков следующий номер.

  • Вы используете систему очереди сообщений. Вещи нужны идентификаторы, прежде чем они попадают в БД

  • Вы создаете несколько элементов и редактируете их перед сохранением. Каждому нужен идентификатор, прежде чем вы нажмете на БД

  • Вы хотите удалить и заново вставить строки. Убедитесь, что вы не подсчитываете свои автоматические идентификаторы и не заканчиваете!

  • Вы не хотите показывать, сколько заказов вы приняли в этом году для каждого пользователя

  • Вы хотите переместить анонимизированные данные из производства в тестирование и сохранить отношения в целости и сохранности. Но не удаляйте все существующие тестовые данные.

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

  • Вы создаете объекты, которые сохраняются, но эфемерны. (неполные заказы), опять же, не используйте все свои целые вещи с вещами, которых больше не существует.

Список бесконечен, и все они - реальные проблемы, которые постоянно случаются с людьми. в отличие от исчерпания дискового пространства из-за немного больших столбцов FK

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

  • о, я не хочу, чтобы клиенты думали, что мы новички. начать с 10 000

  • Мне пришлось импортировать загрузку данных, поэтому я увеличил начальное значение до 1 м, чтобы мы знали, что импортируется

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

  • Я удалил и снова импортировал все данные с новыми идентификаторами. Да, даже журналы аудита.

  • используйте этот номер, который является составным ключом, в качестве идентификатора этой другой вещи


1
На самом деле в этом ответе нет ничего неправильного, но я бы (для предотвращения дальнейших отрицательных голосов), возможно, сделал бы оговорку, что, хотя в реальных приложениях столкновения не возникнут, это теоретически возможно. (Или, может быть, 45+ эксабайтных баз данных более распространены, чем я думал ...). Хотя я думаю, что язык «самая важная причина» немного силен, это то, что я считаю наиболее полезным.
BurnsBA

2
более вероятно, что auto inc int столкнется, чем гид
Ewan

4
-1 для "Вы должны использовать направляющие по умолчанию в любом приложении." Это зависит ™. И, как показали другие, идентификаторы GUID / UUID абсолютно не гарантированы.
Макс Вернон

3
«Это зависит» ответы бесполезны, конечно, будут некоторые странные приложения, где int лучше. Но скорее всего, ваше заявление не является одним из них. GUID - самая уникальная вещь, которую вы можете получить
Ewan

2
Я думаю, что будут некоторые странные приложения, где руководства лучше. Уникальный не самая важная вещь, чтобы рассмотреть. Ваши "недостатки" целых слишком раздуты, и вы не рассматриваете ни одного из многочисленных недостатков направляющих.
Энди

2

Я понимаю, что эти GUID используются для идентификации объектов на уровне приложения. Они также хранятся в качестве первичного ключа на уровне базы данных.

Вот где вы должны остановиться, прямо там и переосмыслить.

Ваш первичный ключ базы данных НИКОГДА не должен иметь делового значения. Это должно быть бессмысленно по определению.

Поэтому добавьте GUID в качестве вашего бизнес-ключа и обычный первичный ключ (обычно длинный int) в качестве первичного ключа базы данных. Вы всегда можете поместить уникальный индекс в GUID, чтобы обеспечить уникальность.

Конечно, это говорит о теории баз данных, но это хорошая практика. Я имел дело с базами данных, где первичные ключи имели деловое значение (например, один клиент думал сохранить некоторые ресурсы базы данных, используя их в качестве номеров сотрудников, номеров клиентов и т. Д. И т. Д.), И это всегда приводит к проблемам.


1
Чем это отличается от запроса на уровне приложения с использованием целочисленного первичного ключа? В этот момент он также используется для идентификации объектов на прикладном уровне. Вам нужен способ идентификации объектов в базе данных на уровне приложения.
icirellik

@icirellik первичный ключ предназначен для внутреннего использования базой данных, для связи родительских и дочерних записей и тому подобного. Он НЕ предназначен для использования логикой приложения, для этого вы используете бизнес-идентификаторы, например, номер продукта или название.
17

2

Всегда используйте сгенерированные базой данных автоинкрементные первичные ключи (PK).

Зачем использовать автоинкремент вместо GUID / UUID?

  • GUID (UUID) не предотвращают коллизии ключей, поскольку они не являются уникальными, и невозможно сделать их уникальными, поскольку они генерируются из многочисленных источников.
  • Идентификаторы GUID не помогают при слиянии, поскольку они значительно увеличивают и без того длительный процесс слияния с очень длинными, нецелыми столбцами PK и FK, на обработку которых уходит много времени. Помните, что для большинства PK будет по крайней мере 1 другая таблица с по крайней мере 2 ключами одинакового размера: это собственный PK и FK обратно к первой таблице. Все должно быть решено в слиянии.

Но как тогда обращаться с осколками, кластерами и т. Д.?

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

PK с 3 столбцами для кластерной таблицы может быть ...

 DB | SH | KEY     |
----|----|---------|
 01 | 01 | 1234567 |

Но что насчет...?

  • Многократные поездки в базу данных - большинству приложений не нужно однозначно идентифицировать создаваемую запись, пока она не будет вставлена ​​в базу данных, поскольку этот поток / сеанс / что-либо работает только с одной за раз. Если приложению действительно нужна эта возможность, используйте созданный приложением временный PK, который не отправляется в базу данных . Пусть база данных затем поместит свой собственный автоинкрементный PK в строку, когда она будет вставлена. Вставки будут использовать временные PK, а обновления и удаления будут использовать постоянные PK, назначенные базой данных.

  • Производительность. Компьютеры могут обрабатывать простые целые числа гораздо быстрее, чем что-либо еще, из-за значительно большей области, если это возможно, значений на элемент в GUID (37) по сравнению с целым числом (10). Помните также, что каждый символ в GUID должен быть сначала преобразован в число, которое будет обработано процессором.

Распространенные злоупотребления первичными ключами У PK есть только одна цель ... абсолютно уникально идентифицировать строку в таблице. Все остальное - слишком распространенное злоупотребление.

Обнаружение пропавших записей

  • Отсутствующие записи не могут быть обнаружены, глядя на ПК. Благослови QA хотя бы за попытку обеспечить качество данных. Тем не менее, они и программисты не понимают, как назначаются ключи в современных системах баз данных, что часто приводит к тому, что пропущенное число в автоинкрементном ПК означает отсутствие данных. Это не потому, что ...
  • Для повышения производительности системы баз данных выделяют блоки чисел в «последовательностях» (пакетах, диапазонах), чтобы минимизировать количество обращений к фактической базе данных в хранилище. Размер этих последовательностей чисел часто находится под контролем DBA, но может не настраиваться отдельно для каждой таблицы.
  • Ключ к выводу ... неиспользуемые числа из этих последовательностей никогда не возвращаются в базу данных, поэтому в числах ПК всегда есть пробелы.
  • Почему бы вам спросить неиспользуемые номера? Потому что различные действия по обслуживанию базы данных могут привести к тому, что последовательности будут отменены. Это такие вещи, как перезапуски, массовые перезагрузки таблиц, некоторые виды восстановления из резервных копий и некоторые другие операции.

Сортировка

  • Сортировка по PK очень подвержена ошибкам, так как большинство людей подумают, что в ней перечислены строки в порядке их создания и соответствуют времени часов. В основном, но не обязательно.
  • Механизмы баз данных оптимизированы для максимальной производительности, и это может означать задержку вставки результатов длительной сложной транзакции для вставки коротких простых, так сказать, «вне очереди».

Что вы думаете о схеме таблицы так, что единственным уникальным столбцом является созданный базой данных автоинкрементный первичный ключ? Особенно для таблиц, которые не имеют внешнего ключа, но чей первичный ключ является внешним ключом для нескольких связанных таблиц?
Рибальд Эдди

Я добавил намного больше к ответу в том же духе. Исходный ответ был неполным из-за приложения Android SE, которое я нахожусь на повешении. Я думаю, что основная перепись приложения находится в разработке.
DocSalvager

Так что, по вашему мнению, было бы хорошо, если бы таблица содержала любое количество одинаковых строк, за исключением их автоинкрементного первичного ключа?
Рибальд Эдди

@RibaldEddie - Насколько то, что БД предназначена для разрешения ... абсолютно. Удаление легко. Когда происходит ваш сценарий, я бы посчитал это ошибкой, которую нужно исправить в программном обеспечении, а затем удалил любую строку. Гораздо более распространенный случай - две записи для одной и той же вещи с немного разными данными, поэтому их необходимо объединить. Если столбец пуст в одной записи и имеет значение в другой, выбор очевиден и может быть автоматизирован. Часто метку даты и времени можно использовать для арбитража автоматического слияния. Некоторые дубликаты требуют, чтобы лицо завершило и проверило объединение на основе бизнес-правил.
DocSalvager

1

Как и у всего, у этого есть свои преимущества и недостатки:

Добро:

  1. Ваши ключи всегда имеют одинаковую длину (очень большие базы данных могут иметь очень большие ключи)

  2. Уникальность в значительной степени гарантирована - даже если вы генерируете их из отдельной системы и / или не прочитали последний идентификатор из базы данных

Плохо:

  1. Как уже упоминалось выше - большие индексы и хранилище данных.

  2. Вы не можете заказать по идентификатору, вы должны заказать что-то еще. Больше индексов, возможно, менее эффективно.

  3. Они менее читабельны. Целые числа обычно легче анализировать, запоминать и вводить для людей. Использование идентификаторов GUID в качестве идентификаторов в предложениях WHERE для нескольких соединенных таблиц может привести к потере сознания.

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


0

Да, вы можете использовать GUID в качестве первичного ключа. Недостатком является размер и быстрая фрагментация индекса.

Если вам не нужна уникальность в базах данных (например, в кластере), предпочтительным является целое число.


Генераторы GUID могут выдавать один и тот же GUID более одного раза, в этом и заключается недостаток. Будут они или нет, зависит от их детализации, в основном от интервала между тактами часов. Например, генератор на основе тактовой частоты может работать только каждые 100 мс, в результате чего 2 идентификатора GUID, запрошенные в течение этих 100 мс на этой машине, будут идентичными. В основном, есть способы избежать этого, но многие генераторы GUID работают полностью вне IP-адреса и / или MAC-адреса и временной метки.
17

0

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

Класс генерирует псевдослучайное (но увеличивающееся со временем) значение Id, которое похоже на Comb GUID .

Ключевое преимущество заключается в том, что он позволяет генерировать значения Id на клиенте, а не использовать значения автоинкрементации, генерируемые на сервере (что требует обратной передачи) с практически нулевым риском дублирования значений.

Сгенерированные значения используют только 8 байтов, а не 16 для GUID, и не зависят от одного конкретного порядка сортировки базы данных (например, Sql Server для GUID ). Значения могут быть расширены для использования всего длинного диапазона без знака, но это может вызвать проблемы с любой базой данных или другим хранилищем данных, которое имеет только целочисленные типы со знаком.

public static class LongIdGenerator
{
    // set the start date to an appropriate value for your implementation 
    // DO NOT change this once any application that uses this functionality is live, otherwise existing Id values will lose their implied date
    private static readonly DateTime PeriodStartDate = new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private static readonly DateTime PeriodEndDate = PeriodStartDate.AddYears(100);
    private static readonly long PeriodStartTicks = PeriodStartDate.Ticks;
    private static readonly long PeriodEndTicks = PeriodEndDate.Ticks;
    private static readonly long TotalPeriodTicks = PeriodEndTicks - PeriodStartTicks;

    // ensures that generated Ids are always positve
    private const long SEQUENCE_PART_PERMUTATIONS = 0x7FFFFFFFFFFF; 

    private static readonly Random Random = new Random();

    private static readonly object Lock = new object();
    private static long _lastSequencePart;

    public static long GetNewId()
    {
        var sequencePart = GetSequenceValueForDateTime(DateTime.UtcNow);

        // extra check, just in case we manage to call GetNewId() twice before enough ticks have passed to increment the sequence 
        lock (Lock)
        {
            if (sequencePart <= _lastSequencePart)
                sequencePart = _lastSequencePart + 1;

            _lastSequencePart = sequencePart;
        }

        // shift so that the sequence part fills the most significant 6 bytes of the result value
        sequencePart = (sequencePart << 16);

        // randomize the lowest 2 bytes of the result, just in case two different client PCs call GetNewId() at exactly the same time
        var randomPart = Random.Next() & 0xFFFF;

        return sequencePart + randomPart;
    }

    // used if you want to generate an Id value for a historic time point (within the start and end dates)
    // there are no checks, compared to calls to GetNewId(), but the chances of colliding values are still almost zero
    public static long GetIdForDateTime(DateTime dt)
    {
        if (dt < PeriodStartDate || dt > PeriodStartDate)
            throw new ArgumentException($"value must be in the range {PeriodStartDate:dd MMM yyyy} - {PeriodEndDate:dd MMM yyyy}");

        var sequencePart = GetSequenceValueForDateTime(dt.ToUniversalTime());
        var randomPart = Random.Next() & 0xFFFF;
        return ( sequencePart << 16 ) + randomPart;
    }

    // Get a 6 byte sequence value from the specified date time - startDate => 0 --> endDate => 0x7FFFFFFFFFFF
    // For a 100 year time period, 1 unit of the sequence corresponds to about 0.022 ms
    private static long GetSequenceValueForDateTime(DateTime dt)
    {
        var ticksFromStart = dt.ToUniversalTime().Ticks - PeriodStartTicks;
        var proportionOfPeriod = (decimal)ticksFromStart / TotalPeriodTicks;
        var result = proportionOfPeriod * SEQUENCE_PART_PERMUTATIONS;
        return (long)result;
    }

    public static DateTime GetDateTimeForId(long value)
    {
        // strip off the random part - the two lowest bytes
        var timePart = value >> 16;
        var proportionOfTotalPeriod = (decimal) timePart / SEQUENCE_PART_PERMUTATIONS;
        var ticks = (long)(proportionOfTotalPeriod * TotalPeriodTicks);
        var result = PeriodStartDate.AddTicks(ticks);
        return result;
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.