Які найкращі практики використання GUID в якості основного ключа, зокрема щодо продуктивності?


336

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

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

Я буду використовувати Entity Framework 4.3 і хочу призначити Посібник в коді програми, перш ніж вставляти його в базу даних. (тобто я не хочу дозволяти SQL генерувати Посібник).

Яка найкраща практика створення первинних клавіш на основі GUID, щоб уникнути передбачуваних показів ефективності, пов’язаних із цим підходом?


20
Питання не передбачається. Якщо ваш ПК є кластеризованим, майже кожна вставка може призвести до розбиття сторінки. У сучасних версіях SQL Server це було «зафіксовано» NEWSEQUENTIALID (), але це втрачає перевагу, якщо можна заздалегідь обчислити його. Я настійно рекомендую прочитати GUID-файли в інших місцях, оскільки це занадто широке запитання і, швидше за все, вимагатиме релігійного бою, який триватиме годинами ...
Аарон Бертран

4
Я також додам, що слово сервер неоднозначне, я хочу призначити керівництво на стороні сервера (не хочу дозволити SQL створювати GUID) .
Ерік Філіпс

Це запитання має схожість із цим "sql-server-guide-sort-алгоритмом-чому" stackoverflow.com/questions/7810602/…
Клінтон Уорд

Відповіді:


495

GUID можуть здатися природним вибором для вашого первинного ключа - і якщо ви дійсно повинні, ви, ймовірно, можете заперечити використовувати його для ОСНОВНОГО КЛЮЧА таблиці. Я настійно не рекомендую цього робити - це використовувати стовпчик GUID як ключ кластеризації , який SQL Server робить за замовчуванням, якщо ви конкретно не скажете цього.

Дійсно потрібно тримати окремо два питання:

  1. первинний ключ є логічною конструкцією - один з ключів - кандидатів , які однозначно і надійно ідентифікують кожен рядок в таблиці. Це може бути все що завгодно, насправді - INTa GUID, a, string - вибирайте те, що має найбільше значення для вашого сценарію.

  2. ключ кластеризації (стовпець або стовпці , які визначають «кластерний індекс» на столі) - це фізичне зберігання пов'язаних річ, і тут, невеликий, стабільний, постійно збільшується тип даних ваш кращий вибір - INTабо BIGINTяк ваш опція за замовчуванням.

За замовчуванням первинний ключ таблиці SQL Server також використовується як ключ кластеризації - але це не повинно бути таким! Я особисто бачив значні підвищення продуктивності, коли розбивали попередній первинний / кластерний ключ на основі GUID на два окремі ключі - первинний (логічний) ключ GUID та кластеризаційний (замовляючий) ключ в окремому INT IDENTITY(1,1)стовпчику.

Як Кімберлі Тріпп - Королева індексації - та інші багато разів заявляли - а, GUIDоскільки кластерний ключ не є оптимальним, оскільки через його випадковість це призведе до масивної фрагментації сторінок та індексів та загалом до поганої продуктивності.

Так, я знаю - є newsequentialid()SQL Server 2005 і новіші версії, але навіть це не є справді і повністю послідовним, і, таким чином, також страждає від тих же проблем, що і GUID- лише трохи менш помітно.

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

Швидкий розрахунок - використання INTпроти , GUIDяк первинний і кластер Key:

  • Базова таблиця з 1'000'000 рядками (3,8 Мб проти 15,26 МБ)
  • 6 некластеризованих індексів (22,89 МБ проти 91,55 МБ)

ВСЕ: 25 МБ проти 106 МБ - і це просто на одному столі!

Ще трохи їжі для роздумів - відмінна штука Кімберлі Тріпп - читайте її, читайте її ще раз, перетравлюйте! Насправді це євангеліє з індексації SQL Server.

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

Оновлення: якщо ви хочете, щоб ваш PKGUIDстовпець був основним ключем (але не вашим кластеризуючим ключем), а інший стовпець MYINT( INT IDENTITY) як ваш кластерний ключ - використовуйте це:

CREATE TABLE dbo.MyTable
(PKGUID UNIQUEIDENTIFIER NOT NULL,
 MyINT INT IDENTITY(1,1) NOT NULL,
 .... add more columns as needed ...... )

ALTER TABLE dbo.MyTable
ADD CONSTRAINT PK_MyTable
PRIMARY KEY NONCLUSTERED (PKGUID)

CREATE UNIQUE CLUSTERED INDEX CIX_MyTable ON dbo.MyTable(MyINT)

В основному: вам просто потрібно чітко сказати про PRIMARY KEYобмеження, що воно є NONCLUSTERED(інакше воно створюється як ваш кластерний індекс, за замовчуванням) - і тоді ви створюєте другий індекс, який визначається якCLUSTERED

Це буде спрацьовувати - і це допустимий варіант, якщо у вас є існуюча система, яку потрібно «переробити» для продуктивності. Для нової системи, якщо ви починаєте з нуля, і ви не в сценарії реплікації, я б завжди вибрав ID INT IDENTITY(1,1)свій кластерний первинний ключ - набагато ефективніший за все інше!


2
Це чудова відповідь, я хочу зазначити одне, що можливість генерувати ключ перед вставкою часто корисна. Використання "newsequentialid ()" може допомогти з кластеризацією, але це потребує додаткового зворотного переходу до SQL. Отже, ще одна перевага підходу «сурогатного ключа» полягає в тому, що ви можете генерувати нові ідентифікатори на стороні клієнта з меншими проблемами фрагментації індексу.
Ендрю Текен

2
Як я прочитав це, що, маючи як некластеризований стовпчик унікального ідентифікатора, так і колонку int ідентичності, FK також повинен бути унікальним ідентифікатором? Якщо ви це зробите, коли б ви насправді безпосередньо використовували стовпець посвідчення, чи ні?
pinkfloydx33

2
Невелике запитання, чи повинен тепер GUID використовуватися для приєднання, або int id? Мій інстинкт підказує, що слід використовувати GUID, але я не бачу технічної проблеми з використанням int id ...
Nicolas Belley

3
@marc_s, але в сценарії реплікації, якщо колонка int є тотожною, чи не слід використовувати GUID, оскільки колонка int може повторюватися на всіх пристроях?
Ніколя Беллі

6
@Kipei: основні питання , це ЯКЩО у вас є таке природне значення - то так, ви можете використовувати його в якості первинного ключа. АЛЕ : такі величини, як, DATETIMEнаприклад, НЕ корисні для кластеризації ключа, оскільки вони мають точність лише 3,33 мс, і тому дублікати можуть існувати. Тож у такому випадку вам все-таки потрібен INT IDENTITYзамість цього - тому я зазвичай використовую це за замовчуванням, оскільки frmo мій 20+ років досвіду, дійсно корисний природний ключ навряд чи справді існує ....
marc_s

51

Я використовую GUID як ПК у 2005 році. У цьому світі розподілених баз даних це абсолютно найкращий спосіб об’єднання розподілених даних. Ви можете запускати та забувати злиття таблиць, не турбуючись про те, що вбудовані таблиці поєднуються між таблицями. GUID-приєднання можна скопіювати без будь-якого турботи.

Це моя установка для використання GUID:

  1. ПК = GUID. GUID індексуються аналогічно рядкам, тому таблиці з високими рядками (понад 50 мільйонів записів) можуть потребувати розподілу таблиць або інших методів виконання. SQL Server стає надзвичайно ефективним, тому проблеми щодо продуктивності все менше застосовуються.

  2. PK Guid - це NON-Clustered index. Ніколи не кластеризуйте GUID, якщо це не NewSequentialID. Але навіть тоді перезавантаження сервера спричинить великі перерви в замовленні.

  3. Додайте ClusterID Int до кожної таблиці. Це ваш CLUSTERED Index ..., який упорядковує вашу таблицю.

  4. Приєднання до ClusterIDs (int) є більш ефективним, але я працюю з 20-30 мільйонами таблиць записів, тому приєднання до GUID не помітно впливає на продуктивність. Якщо ви хочете максимальної продуктивності, використовуйте концепцію ClusterID як основний ключ та приєднайтесь до ClusterID.

Ось мій стіл електронної пошти ...

CREATE TABLE [Core].[Email] (
    [EmailID]      UNIQUEIDENTIFIER CONSTRAINT [DF_Email_EmailID] DEFAULT (newsequentialid()) NOT NULL,        
    [EmailAddress] NVARCHAR (50)    CONSTRAINT [DF_Email_EmailAddress] DEFAULT ('') NOT NULL,        
    [CreatedDate]  DATETIME         CONSTRAINT [DF_Email_CreatedDate] DEFAULT (getutcdate()) NOT NULL,      
    [ClusterID] INT NOT NULL IDENTITY,
    CONSTRAINT [PK_Email] PRIMARY KEY NonCLUSTERED ([EmailID] ASC)
);
GO

CREATE UNIQUE CLUSTERED INDEX [IX_Email_ClusterID] ON [Core].[Email] ([ClusterID])
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_Email_EmailAddress] ON [Core].[Email] ([EmailAddress] Asc)

Чи можете ви пояснити обмеження PK_Email? Чому у вас ... NonClustered (EmailID ASC) замість ... Unclustered (ClusterID ASC)?
Філ

2
Будьте впевнені. Дві основні речі, що відбуваються з індексами: 1. Кластеризований на ClusterID - Замовляє вашу таблицю на диску (0% фрагментація). 2. NonClustered on EmailID - Індексує поле EmailID для прискорення пошуку ідентифікаторів GUID. Пошук поля GUID поводиться рядково-ish, тому пошук по EmailID буде повільним без індексу.
Роберт Дж. Добрий

@ RobertJ.Good Я бачив, як цей метод обговорювався раніше, тобто додавання сурогатного клавіша Int для кластеру. Але я не можу ніде знайти, що показує збільшення продуктивності, коли сурогатний ключ кластеризований індекс за допомогою купи. Чи є у вас посилання на базові дані?
Дейл К

1
Привіт @DaleBurrell, кластерний індекс запобігає фрагментації таблиці. Підвищення продуктивності відбувається, коли таблиця природно зростає в порядку на диску з низькою фрагментацією.
Роберт Дж. Добрий

@ RobertJ.Good Це веб-додаток? Що ви використовуєте в url / hrefs? guid чи int?
dariol

10

В даний час я розробляю веб-додаток із EF Core, і ось такий шаблон я використовую:

Всі мої класи (таблиці) та int PK та FK. У мене з'явився додатковий стовпець з типом Guid (згенерований конструктором c #) з некластеризованим індексом на ньому.

Усі з'єднання таблиці в EF керуються за допомогою клавіш int, а весь доступ ззовні (контролери) здійснюється за допомогою Посібників.

Це рішення дозволяє не показувати клавіші int на URL-адресах, але підтримувати модель акуратною та швидкою.


Чи потрібно щось зробити для налаштування цілого pK як кластеризованого, як анотації даних, чи це просто налаштовано автоматично?
Аллен Ван

Яку назву майна ви використовуєте для Guid one?
Тронг Фан

3

Якщо ви використовуєте GUID як первинний ключ та створюєте кластерний індекс, я пропоную використовувати для нього значення NEWSEQUENTIALID () за замовчуванням


чому б ти це робив?
truefafa

3

Це посилання говорить про це краще, ніж я міг, і допомогло у прийнятті рішень. Зазвичай я вибираю int як основний ключ, якщо я не маю конкретної потреби, і я також дозволяю SQL серверу автоматично генерувати / підтримувати це поле, якщо у мене немає певних причин цього не робити. Насправді питання щодо ефективності потрібно визначати на основі конкретного додатка. Тут є багато факторів, включаючи очікуваний розмір дБ, але не обмежуючись ними, належну індексацію, ефективний запит тощо. Хоча люди можуть не погодитися, я думаю, що в багатьох сценаріях ви не помітите різниці в будь-якому варіанті, і вам слід вибрати те, що більше підходить для вашої програми та що дозволяє розвиватись простіше, швидше та ефективніше (якщо ви ніколи не заповнюєте додаток яка різниця робить решту :).

https://web.archive.org/web/20120812080710/http://databases.aspfaq.com/database/what-should-i-choose-for-my-primary-key.html

PS Я не впевнений, чому б ви використовували композитний ПК або яку вигоду, на вашу думку, давали б вам.


Повністю згоден!! Але це означає, що якщо у мене є GUID як PK або складений ПК з GUID та іншим полем, буде так само правильно?
VAAA

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

1
До речі, це питання - одне з найбільш поляризуючих та обговорюваних питань, і тому вкрай важко отримати відповідь на те, що ви будете почувати себе на 100% комфортно. Будь-який метод має компроміси, тож удачі :)
Метт

1

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

  1. https://www.sqlskills.com/blogs/kimberly/disk-space-is-cheap/
  2. https://www.sqlskills.com/blogs/kimberly/guids-as-primary-keys-andor-the-clustering-key/

0

Наявність послідовних ідентифікаційних кодів робить ЛОКУ простішим для хакера чи шахрая даних компрометацію вашого сайту та даних. Майте це на увазі, вибираючи ПК для веб-сайту.


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

Звичайно, якщо ви знаєте, що ідентифікаційні номери цілі, ви можете вгадувати послідовно записи в БД. Отже, якщо ви запитуєте один елемент, ви можете сказати, що наступним елементом є pk + 1. Якщо у вас є випадкові ПОМИЛКИ, він не буде слідувати шаблону. Бути майже неможливо запитувати інші записи, ніж ті, які ви раніше запитували (І знайте, ПК).
DaBlue

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

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

2
Ви можете використовувати GUID для пошуку запису на веб-сторінці, тобто не PK таблиці. Використання параметра запиту на веб-сайті не повинно визначати, як ви структуруєте схему БД. ПК не має нічого спільного з вхідними даними та параметрами в інтерфейсі або резервній системі.
Панос Родітакіс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.