Використання 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 та цілими числами як первинні ключі.

Редагувати 11.06.18

Я повірив, що Посібники для моїх вимог більше підходять, ніж ints. Цього дня я більше використовую CQRS, і GUID-файли більше вписуються.

Я помічаю, що деякі розробники моделюють GUID як рядки в доменній моделі, наприклад тут: https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/ Buyer.cs - в цьому випадку: IdentityGuid - це 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 існує схожа, але дещо інша концепція, яка називається UUIDs "Універсально унікальні ідентифікатори". Назви взаємозамінні для будь-якого практичного використання.

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

Деякі факти про-GUID:

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

Деякі неприємності з GUID

  • Вони великі, 16 байт кожен
  • Вони вийшли з ладу, тому ви не можете сортувати за ідентифікатором і сподіваєтесь отримати порядок вставки, як можна, на ідентифікатори з автоматичним збільшенням
  • Вони більш громіздкі для роботи, особливо з невеликими наборами даних (як-от шукати таблиці)
  • Нова реалізація GUID є більш надійною на SQL Server, ніж у бібліотеці C # (ви можете мати послідовні GUIDS з SQL Server, у C # - випадкова)

GUID дозволить збільшити ваші індекси, тому вартість дискового простору на індексацію стовпця буде вище. Випадкові GUID-файли будуть фрагментувати ваші індекси.

Якщо ви знаєте, що не збираєтеся синхронізувати дані з різних мереж, GUID можуть мати більше витрат, ніж коштують.

Якщо у вас є необхідність вводити дані з іноді підключених клієнтів, вони можуть бути набагато надійнішими для запобігання зіткнення ключів, ніж покладатися на встановлення діапазонів послідовностей для цих клієнтів.


18
Я розумію, що GUID є синонімом UUID. UUID - це стандартна назва. GUID - це те, що Microsoft винайшов їх до RFC 4122 .
JimmyJames

13
"Вони вийшли з ладу, тому ви не можете сортувати за ідентифікатором і сподіваєтесь отримати порядок вставки, як ви можете, на автодокументи з автоматичним збільшенням" Чесно кажучи, мені не комфортно покладатися на це і з регулярними ідентифікаторами. Хоча в крайньому випадку можливе, щоб нижній ідентифікатор пізніше перейшов на диск, я б краще покластися на корисні дані про сортування, як-от часова мітка вставки. Ідентифікатори повинні трактуватися як адреси пам'яті - все має одне, але саме значення безглуздо. Використовуйте їх не більше ніж для крадіжок. Тим більше, що якщо у вас є велике навантаження, порядок вставки не гарантується.
Годинник-муза

8
@CortAmmon За даними Wikipedia та RFC 4122 , вони є синонімами. П. Ліч з Microsoft був одним із творців RFC. Я думаю, що з моменту створення RFC, вони однакові. З RFC: "UUID (Універсально унікальний ідентифікатор), також відомий як GUID (глобально унікальний ідентифікатор)." Я думаю, що також корисно зазначити, що 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 доступний як "примітивний тип" у налаштуваннях вашого домену агностики, я б використовував його; це дозволяє підтримувальному контексту вибирати відповідні оптимізації, які можуть бути доступними.

Однак ви повинні визнати, що представлення ідентифікатора, як у пам'яті, так і на сховищі, - це рішення, яке ви приймаєте під час своєї реалізації, і тому вам слід вжити заходів для того, щоб друкований код коду поєднався з цим рішення невелике - див. Парнас 1972 .


20
+1 за "у вас вже закінчилося місце на диску до того часу, як це станеться".
w0051977

2
Я вважаю, що концепція " детерміновано створеного UUID " є важливою (див. Vault 2)
алк

Дійсно, можливість перерахувати UUID / GUID на основі інших даних - це величезна допомога, особливо для виявлення дублікатів. Я колись побудував систему обробки повідомлень, яка зберігала повідомлення та просувала їх через конвеєр обробки. Я створив хеш повідомлення і використав це як основний ключ у всій системі. саме це, саме по собі, вирішило мені багато питань, щоб визначити повідомлення, коли нам довелося масштабувати масштаб.
Ньютопський

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

Я повертаюся до своїх історичних питань. Перш ніж приймати; Ви можете подивитися на мою редакцію?
w0051977

11

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

Переваги GUID як основний ключ:

  • Ви можете копіювати дані між різними фрагментами кластеру і не потрібно турбуватися про зіткнення ПК.
  • Це дозволяє вам знати ваш основний ключ, перш ніж вставити будь-які записи.
  • Спрощує логіку транзакцій для вставки дочірніх записів.
  • Не можна легко здогадатися.

У наведеному прикладі:

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-адресі. Для
запобігання

1
Є ще один випадок використання: важкі бази даних OLTP, в яких блокування послідовності є вузьким місцем. За словами мого друга Oracle DBA, це не так рідко, як це звучить, вам навіть не потрібні великі масштаби або кластери для цього. • Зрештою, зважте свої плюси та мінуси (і не плутайте плюси та мінуси UUID з плюсами / мінусами, які не є специфічними для UUID, як це роблять деякі плакати) та виміряйте .
mirabilos

1
Якщо ви використовуєте newsequentialid, тоді вам потрібно перейти до db, щоб отримати ідентифікатор (як, наприклад, ідентифікаційний код), чи не так? Яка тут користь.
w0051977

1
@mirabilos Щоб було зрозуміло, коли я кажу, що ми жахливі, у нас з'явилися вставки, які займали хвилини в ряд. Це стартувало нормально, але після того, як було 10 тисяч тисяч рядків, воно пішло боком дійсно швидко. Якщо це не очевидно, 10 тисяч тисяч рядків - це дуже маленька таблиця.
JimmyJames

4

Я б сказав, що ні, не використовуйте GUID як основний ключ. Зараз я фактично маю справу з такою БД, і вони є однією з головних причин проблем продуктивності.

Додаткові 12 байт швидко накопичуються; пам’ятайте, що більшість ПК будуть ФК в інших таблицях, і лише три FK в таблиці тепер у вас є 48 байтів додатково для кожного ряду. Це підсумовується в таблиці та в індексах. Він також додається в дисковод вводу / виводу. Ці зайві 12 байт потрібно прочитати та записати.

І якщо ви не використовуєте послідовних посібників, і ПК є кластеризованими (що відбувається за замовчуванням), SQL час від часу доведеться переміщувати цілі сторінки даних, щоб видавити їх більше в потрібне місце. Для високошвидкісної бази даних транзакцій з великою кількістю вставок, оновлень та видалень речі швидко розмиваються.

Якщо вам потрібен якийсь унікальний ідентифікатор для синхронізації або щось подібне, додайте стовпчик із орієнтиром. Просто не робіть це ПК.


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

Це, безумовно, найважливіша причина використання GUID.

Той факт, що ви можете створити унікальний ідентифікатор, не знаючи про код і не спілкуючись зі своїм постійним шаром, є величезною перевагою.

Ви можете бути впевнені, що об'єкт Person, який ви щойно створили на своєму сервері, ПК, ноутбуці, автономному пристрої або будь-якому іншому, є унікальним на всіх ваших серверах у всьому світі, як би не було розподілено.

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

Ні, ви ніколи не зіткнетесь.

Так, вставки можуть бути дещо повільнішими, оскільки індекс, можливо, знадобиться.

Так, він більший за int.

  • редагувати. довелося знімати перед тим, як закінчити.

Я знаю, що багато людей сильно ставляться до автоматичних вхідних даних, і це спірна тема з DBA

Але я дійсно не можу стверджувати достатньо сильно, наскільки чудові посібники. Ви повинні використовувати посібники за замовчуванням у будь-якій програмі.

автоматичні вхідні дані мають безліч недоліків

  • Ви використовуєте розподілений db No-Sql. Ви просто не можете поговорити з усіма іншими інстанціями, щоб дізнатися, що таке наступне число.

  • Ви використовуєте систему черги повідомлень. Речі потребують ідентифікаторів, перш ніж вони потраплять на db

  • Ви створюєте кілька елементів та редагуєте їх перед збереженням. Кожному потрібен ідентифікатор, перш ніж ви натиснете на db

  • Ви хочете видалити та повторно вставити рядки. Переконайтеся, що ви не рахуєте свої автоматичні ідентифікатори та закінчуєтесь!

  • Ви не хочете виставляти кількість замовлень, які ви взяли цього року кожному користувачеві

  • Ви хочете перемістити анонімізовані дані з виробництва, щоб перевірити і зберегти стосунки неушкодженими. Але не видаляйте всі існуючі дані тесту.

  • Ви хочете об'єднати свій єдиний продукт-орендар у базу даних, що має багато оренду, але кожен має замовлення 56.

  • Ви створюєте об'єкти, які зберігаються, але ефемерні. (неповні замовлення) знову, не використовуйте всі свої вкладиші з речей, яких більше немає.

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

Нарешті, масовою проблемою з ints є те, що ви їх закінчили !!! Ок, теоретично ви не робите, є вантажі. Але на практиці ви так робите, тому що люди не ставляться до них як до випадкових чисел без сенсу. вони роблять подібні речі

  • о, я не хочу, щоб клієнти думали, що ми нові. старт з 10 000

  • Мені довелося імпортувати навантаження даних, тому я просто збільшив насіння на 1 м, щоб ми знали, що імпортується

  • нам потрібні дані категорії. кожен період починається з наступного мільйона, тому ми можемо використовувати перші цифри як магічне число

  • Я знову видалив і повторно імпортував усі дані з новими ідентифікаторами. Так, навіть журнали аудиту.

  • використовувати це число, яке є складовим ключем, як ідентифікатор цієї іншої речі


1
З цією відповіддю немає нічого фактично неправильного, але я б хотів (щоб уникнути подальших голосів), можливо, явно зауважую, що, хоча додатки в реальному житті не стикаються зіткненнями, це теоретично можливо. (А може, 45+ екстрабайтових баз даних є більш поширеними, ніж я думав ...). Хоча я вважаю, що мова "найважливіша причина" трохи сильна, саме це я вважаю найкориснішим.
BurnsBA

2
більше ймовірність, що авто-інт зіткнеться, ніж вказівник
Ewan

4
-1 для "Ви повинні використовувати посібники за замовчуванням у будь-якій програмі." Це залежить ™. Як показали інші, GUID / UUID, абсолютно не гарантовано є унікальними.
Макс Вернон

3
Відповіді "Це залежить" марні, впевнені, що там будуть якісь незвичайні програми, де інт кращий. Але шанси на те, що ваша заявка не одна з них. GUID - це найунікальніше, що можна отримати
Ewan

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

2

Я усвідомлюю, що ці GUID використовуються для ідентифікації об'єктів на рівні програми. Чи вони також зберігаються як первинний ключ на рівні бази даних.

Ось де слід зупинитися, прямо там і переосмислити.

Первинний ключ вашої бази даних НІКОЛИ не повинен мати ділового значення. Це має бути безглуздим за визначенням.

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

Це, звичайно, теорія баз даних, але це також хороша практика. Я мав справу з базами даних, де первинні ключі мали ділове значення (один клієнт думав зберегти деякі ресурси бази даних, наприклад, використовуючи їх у якості службових номерів, номерів клієнтів тощо), і це завжди призводить до проблем.


1
Чим це відрізняється від запиту від рівня програми за допомогою цілого первинного ключа? У цей момент він також використовується для ідентифікації об'єктів на рівні додатків. Вам потрібен спосіб ідентифікації об'єктів у базі даних із рівня програми.
icirellik

@icirellik первинний ключ призначений для внутрішнього використання в базі даних, для зв'язку батьківських і дочірніх записів тощо. Він НЕ призначений для використання за допомогою логіки програми, ви використовуєте для цього ідентифікатори підприємств, наприклад номер продукту чи назву.
jwenting

2

Завжди використовуйте базові ключі, автоматично наростаючі первинні ключі (ПК).

Навіщо використовувати автоматичне збільшення замість GUID / UUID?

  • GUID (UUID) не запобігають зіткненням ключів, оскільки вони не є унікальними, і немає можливості зробити їх унікальними, оскільки вони генеруються з численних джерел.
  • GUID не допомагають при злитті, оскільки вони значно збільшують і без того тривалий процес злиття з надзвичайно довгими цілими стовпцями PK та FK, які потребують багато часу для обробки. Пам’ятайте, що для більшості ПК буде принаймні 1 інша таблиця з принаймні 2 клавішами однакового розміру: це власний ПК та FK назад до першої таблиці. Усі повинні бути вирішені об'єднаними.

Але як тоді поводитися з черепками, гронами тощо?

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

3-стовпецький ПК для кластеризованої таблиці може бути ...

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

А як же ...?

  • Кілька поїздок до бази даних - Більшість програм не потребують однозначної ідентифікації запису, який створюється, поки він не вставляється в базу даних, оскільки цей потік / сеанс / все, що працює лише один на один. Якщо програмі дійсно потрібна ця здатність, використовуйте додаток, створений тимчасовим ПК, який не надсилається до бази даних . Нехай тоді база даних ставить власну ПК з автоматичним збільшенням у рядок, коли вона вставлена. Вставки використовуватимуть тимчасовий ПК, тоді як оновлення та видалення використовуватимуть постійний ПК, призначений базою даних.

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

Поширені зловживання ПК первинних ключів мають лише одну мету ... абсолютно однозначно визначити рядок у таблиці. Все інше - це занадто поширене зловживання.

Виявлення відсутніх записів

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

Сортування

  • Сортування за ПК дуже схильне до помилок, оскільки більшість людей вважають, що вони перераховують рядки в тому порядку, в якому вони були створені, і це відповідає часу годинника. Переважно, але не обов'язково.
  • Двигуни бази даних оптимізовані для максимальної продуктивності, і це може означати затримку вставки результатів тривалої складної транзакції для того, щоб вставити короткі прості з них, так би мовити.

Які ваші думки щодо схеми таблиці такі, що єдиний унікальний стовпець - це створений базою даних автоматичний приріст первинного ключа? Зокрема, для таблиць, у яких немає зовнішнього ключа, але основним ключем є зовнішній ключ для декількох пов’язаних таблиць?
RibaldEddie

Я відповів набагато більше відповіді. Оригінальна відповідь була неповною через додаток Android SE, на якому я зависаю. Я думаю, що в процесі розробки головне перезапис програми.
DocSalvager

Отже, на ваш погляд, було б добре, щоб таблиця містила будь-яку кількість рядків, які були однаковими, крім збереженого для їх автоматичного збільшення первинного ключа?
RibaldEddie

@RibaldEddie - Що стосується того, що створена БД, щоб дозволити ... абсолютно. Видалення просте. Коли трапляється ваш сценарій, я вважаю, що помилка повинна бути виправлена ​​в програмному забезпеченні, а потім видалити будь-який рядок. Набагато більш поширений випадок, однак, це два записи для однієї речі з дещо різними даними, тому їх необхідно об'єднати. Якщо стовпець порожній в одному записі і має значення в іншому, вибір очевидний і може бути автоматизований. Часто марку дати можна використовувати для арбітражу автоматизованого злиття. Деякі дублікати вимагають від людини закінчити і перевірити злиття на основі правил бізнесу.
DocSalvager

1

Як і все, для цього є переваги та недоліки:

Добре:

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

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

Погане:

  1. Як було сказано набагато вище - більші індекси та сховище даних.

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

  3. Вони менш зрозумілі для людини. Цілі особи, як правило, простіше розбирати, запам'ятовувати та вводити людям. Використання GUID в якості ідентифікаторів у пунктах WHERE у кількох приєднаних таблицях може змусити вашу голову танути.

Як і все, використовуйте їх там, де це доречно, не будьте догматичними - у багатьох ситуаціях краще збільшувати цілі числа, інколи GUID - це чудово.


0

Так, ви можете використовувати GUID як основний ключ. Нижня сторона - це розмір та швидка фрагментація індексу.

Якщо вам не потрібна унікальність для всіх баз даних (наприклад, кластер), ціле число є кращим.


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

0

Ось мій погляд на це питання - рішення - це на півдорозі між значеннями GUID та int, використовуючи найкращі з обох.

Клас генерує псевдовипадкове (але з часом збільшується) значення id, яке схоже на гребінковий GUID .

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

Згенеровані значення використовують лише 8 байт, а не 16 для GUID, і не залежать від одного конкретного порядку сортування бази даних (наприклад, сервер Sql для 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.