Індексація PK GUID в SQL Server 2012


13

Мої розробники налаштували свою програму для використання GUID в якості ПК для майже всіх своїх таблиць, і за замовчуванням SQL Server встановив кластерний індекс для цих ПК.

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

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

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


1
Як створюються GUID? NEWID чи NEWSEQUENTIALID?
swasheck

6
Кластерні орієнтири та результати вставки мають бути лише у реченні, якщо слово, що безпосередньо передує "виступу", зведено до мінімуму
billinkc

2
Візьміть цих розробників на обід і поясніть їм, що якщо вони знову використовуватимуть NEWID () в якості основного ключа, ви будете звинувачувати в них низьку ефективність. Вони дуже швидко запитають вас, що робити, щоб цього не допустити. У цей момент ви скажете використовувати замість цього IDENTITY (1,1). (можливо, незначне надмірне спрощення, але 9 разів із 10, які спрацюють).
Макс Вернон

3
Причина нашої ненависті до керівництва полягає в тому, що вони широкі (16 байт), а коли не створюються, newsequentialidє випадковими. Кластерні клавіші найкраще, коли вони вузькі та збільшуються. GUID - навпаки: жирний і випадковий. Уявіть, що полиця майже наповнена книжками. Поставляється OED, і через випадковість напрямних він вставляється посередині полиці. Щоб все було впорядковано, праву половину книг потрібно покарати на нове місце, що є трудомістким завданням. Це те, що GUID робить для вашої бази даних та знищує продуктивність.
billinkc

7
Спосіб вирішити проблему використання унікальних ідентифікаторів - це повернутися до креслярської дошки та не використовувати унікальні ідентифікатори . Вони не страшні, якщо система невелика, але якщо у вас є принаймні кілька мільйонів + таблиць рядків (або будь-яка таблиця, більша за цю), ви просто не збираєтеся роздавлювати за допомогою унікальних ідентифікаторів для ключів.
Джон Сейгель

Відповіді:


20

Основними проблемами з GUID, особливо непослідовними, є:

  • Розмір ключа (16 байт проти 4 байт для INT): Це означає, що ви зберігаєте в 4 рази більше, ніж кількість даних у вашому ключі, а також додаткове місце для будь-яких індексів, якщо це ваш кластерний індекс.
  • Фрагментація індексу: утримувати непослідовний стовпчик GUID дефрагментованим практично неможливо через повністю випадковий характер ключових значень.

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

Основне питання навколо GUID - наскільки вони необхідні. Розробникам вони подобаються, оскільки вони забезпечують глобальну унікальність, але рідкісний випадок, коли такий унікальність необхідний. Але врахуйте, що якщо ваша максимальна кількість значень менше 2,147,483,647 (максимальне значення 4-байтного підписаного цілого числа), ви, ймовірно, не використовуєте відповідний тип даних для свого ключа. Навіть використовуючи BIGINT (8 байт), ваше максимальне значення становить 9,223,372,036,854,775,807. Цього зазвичай достатньо для будь-якої не глобальної бази даних (і багатьох глобальних), якщо вам потрібне деяке значення автоматичного збільшення для унікального ключа.

Нарешті, що стосується використання купи порівняно з кластерним індексом, якщо ви суто записуєте дані, купа буде найбільш ефективною, оскільки ви мінімізуєте накладні витрати на вставки. Однак купи на SQL Server надзвичайно неефективні для пошуку даних. Мій досвід показав, що кластерний індекс завжди бажаний, якщо у вас є можливість оголосити його. Я бачив, як додавання кластеризованого індексу до таблиці (4 мільярди + записи) покращує загальну ефективність вибору в 6 разів.

Додаткова інформація:


13

Немає нічого поганого з GUID як ключі та кластери в системі OLTP (якщо тільки у вас на столі немало індексів, які страждають від збільшення розміру кластера). Власне кажучи, вони набагато масштабніші, ніж стовпці Ідентифікатор.

Поширена думка, що GUID - це велика проблема в SQL Server - багато в чому це просто неправильно. Власне кажучи, GUID може бути значно більш масштабованим на коробках з більш ніж 8 ядрами:

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

Ну, і нарешті: чому ви хочете в першу чергу індексу кластерів? Якщо вас турбує система OLTP з безліччю невеликих індексів, вам, швидше за все, краще з купою.

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

  1. Сторінка розділяє вартість вводу / виводу диска
  2. Половина повних сторінок не настільки ефективна, як пам’ять
  3. Це призводить до того, що сторінки зберігаються не в порядку, що робить послідовні введення / виведення менш ймовірними

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

Оголошення 1) Якщо ви хочете масштабу, тоді ви можете дозволити собі придбати введення / виведення. Навіть дешевий SSD-диск Samsung / Intel 512 Гб (при кількох доларах США / ГБ) отримає вам понад 100 КБ IOPS. Ви не будете споживати цього незабаром на 2 розетковій системі. І якщо вам доведеться зіткнутися з цим, придбайте ще один, і ви налаштовані

Оголошення 2) Якщо ви видалите таблицю, у будь-якому випадку у вас буде половина повних сторінок. І навіть якщо ви цього не зробите, пам'ять дешева і для всіх, окрім найбільших OLTP-систем - гарячі дані повинні вміщуватися там. Потрібно упакувати більше даних на сторінки, не оптимізуючи їх під час пошуку масштабу.

Оголошення 3) Таблиця, побудована з часто розділених сторінок, сильно фрагментованих даних робить випадкові введення-виведення з тією ж швидкістю, що і послідовно заповнені таблиці

Що стосується вступу, є два основних типи приєднання, які ви, швидше за все, побачите в OLTP, як завантаженість: Хеш і петля. Давайте розглянемо кожного по черзі:

Хеш-з'єднання: хеш-з'єднання передбачає, що маленька таблиця сканується, а більша - зазвичай шукається. Невеликі таблиці, ймовірно, знаходяться в пам’яті, тому введення-виведення тут не стосується вас. Ми вже торкнулися того факту, що прагнення мають таку саму вартість у фрагментованому індексі, як у не фрагментованому індексі

Приєднання до циклу: буде показана зовнішня таблиця. Така ж вартість

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

Тепер у вас можуть бути законні сканування діапазону (особливо при приєднанні до зовнішніх ключів), і в цьому випадку фрагментовані дані менш "запаковані" порівняно з не фрагментованими даними. Але давайте розглянемо, які приєднання ви, ймовірно, побачите у добре індексованих даних 3NF:

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

  2. Навпаки

Оголошення 1) У цьому випадку ви збираєтесь одночасно шукати основний ключ - приєднавшись до n до 1. Фрагментація чи ні, однакова вартість (одна спроба)

Оголошення 2) У цьому випадку ви приєднуєтесь до одного і того ж ключа, але можете отримати більше, ніж один рядок (діапазон пошуку). Приєднання в цьому випадку становить від 1 до n. Однак в іноземній таблиці, яку ви шукаєте, ви шукаєте ключ SAME, який так само ймовірно буде на тій самій сторінці у фрагментованому покажчику, що і на не фрагментованій.

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

Звичайно, можливо, ви працюєте на віртуальній машині в якомусь SAN в якомусь банку, який коштує дешево і на високому рівні. Тоді вся ця порада буде втрачена. Але якщо це ваш світ, масштабування, ймовірно, не те, що ви шукаєте - ви шукаєте продуктивність та високу швидкість / вартість - які є різними речами.


1
Коментарі не для розширеного обговорення; ця розмова була переміщена до чату .
Пол Білий 9

5

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

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

Велика проблема з IDENTITY (або послідовністю, або будь-чим, що генерується в БД) полягає в тому, що це жахливо повільно, оскільки для створення ключа потрібна зворотна поїздка до БД, і це автоматично робить вузьке вузьке місце у вашій БД; зробити дзвінок БД, щоб почати використовувати ключ. Створення GUID вирішує це, використовуючи додаток для створення ключа, він гарантовано є унікальним у всьому світі (за визначенням), і шари програми можуть таким чином використовувати його для передачі запису БЕРЕГО, перед тим як здійснити обхід DB.

Але я схильний використовувати альтернативу GUIDs Мої особисті переваги до типу даних тут - це глобально унікальний BIGINT, що генерується додатком. Як можна робити це? У самому тривіальному прикладі ви додаєте невелику, ДУЖЕ легку функцію у свій додаток для хешування GUID. Якщо припустити, що ваша хеш-функція є швидкою та відносно швидкою (див. CityHash від Google на одному прикладі: http://google-opensource.blogspot.in/2011/04/introducing-cityhash.html - переконайтеся, що ви виконали всі кроки компіляції правильно, або варіант FNV1a http://tools.ietf.org/html/draft-eastlake-fnv-03 для простого коду), це дає вам перевагу як унікальних ідентифікаторів, створених додатком, так і 64-бітного ключового значення, з яким процесори краще працюють .

Є й інші способи генерування BIGINT, і в обох цих альго існує ймовірність хеш-зіткнень - читати та приймати свідомі рішення.


2
Я пропоную вам відредагувати свою відповідь як відповідь на питання ОП, а не (як зараз) як відповідь на відповідь Томаса. Ви все ще можете виділити відмінності між Томасом (, MikeFal) та вашою пропозицією.
ypercubeᵀᴹ

2
Будь ласка, зверніться до своєї відповіді на запитання. Якщо ви цього не зробите, ми видалимо його для вас.
JNK

2
Дякую за коментарі Марк. Коли ви редагуєте свою відповідь (що, на мою думку, надає дуже хороший контекст), я змінив би одне: IDENTITY не вимагає додаткової подорожі на сервер, якщо ви обережні з INSERT. Ви завжди можете повернути SCOPE_IDENTITY () у партії, яка викликає
ВСТУП

1
Що стосується "це жахливо повільно, оскільки для створення ключа потрібна поїздка в БД", - ви можете захопити стільки, скільки вам потрібно, за одну туру.
АК

Щодо "ви можете захопити стільки, скільки вам потрібно за одну туру" - Ви не можете цього зробити за допомогою стовпців "ІДЕНТИМЕНТ" або будь-якого іншого методу, де ви в основному використовуєте DEFAULT на рівні бази даних.
Avi Cherry
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.