Целые vs GUID vs Естественные и Суррогатные ключи

 

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

Естественный vs Суррогатный ключи

Джо Целко (Joe Celko) утверждает что мы должны использовать естественные ключи, созданные из атрибутов записи, а не заниматься присвоением произвольных значений записям в таблицах. Я согласен с ним. Но! В то же время мы должны стараться хранить ключи легкими/маленькими, вместо того, чтобы создавать массивные составные ключи, чьи значения в свою очередь должны будут храниться в других таблицах для возможности соединения таблиц, поддержания их целостности. Как я выбираю какой способ использовать? Легко, я использую оба.

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

Целочисленный ряд vs GUID

Прежде чем начнем, могу я попросить произносить «GUID» как «гуид», а не «гуайди»? Кстати, это тоже предмет жарких споров, но ирония в том, что лагерь борцов за суррогатные ключи борется против себя же.

В моей практике было активное использование обоих вариантов, однако в последнее время нарисовался победитель по всем статьям, и это – GUID.

Главным аргументом в этой баталии долгое время оставался размер ключа. 4 байта для Int и 16 байт для GUID. Да, GUID занимает 16 байт, а не 32. Основным аргументом является то, что с 4х-байтными ключами база работает быстрее. Да, действительно, использование целочисленных суррогатных ключей может быть незначительно быстрее, но при этом привносится целый комплекс неудобств связанных с самой природой целых чисел.

Еще один аргумент против GUID состоит в том, что индекс получается фрагментированным и сильно бьет страницы. Однако это может быть легко исправлено используя GUID “COMB”. Данный прием позволяет значительно увеличить производительность при вставке и объединения таблиц.

Результат, полученный с помощью данного кода:

Да, последний блок одинаковый, но это, потому что они были сформированы в одно время. Разрешающая способность по времени у SQL сервера в районе 1/300 секунды.

Начиная отвечать на эти аргументы, сразу заметим, что базы данных умные. Когда происходит сравнение ключей, движок базы не сравнивает все 16 байт для того, чтобы найти различия. Процесс сравнения останавливается при первом же расхождении. Далее, как часто в результате операции select+join вы планируете получать десятки тысяч, миллионы строк в ответ, когда действительно тип первично ключа играет роль. Однако чаще всего требуются десятки и сотни строк для работы пользователя и в дальнейшем узким местом является не работа по сравнению ключей, а построение всех нужных ассоциаций и создание новых объектов.

Следующим аргументом за GUID является то, что в любой средней/большой системе, как например PayPal, eBay и так далее, требуется использовать 8и-байтовое число, т.е. BigInt, в противном случае вы достаточно быстро исчерпаете все уникальные значения для хранения данных. Бывали ли у вас случаи, когда необходимо было быстро перевести схему базы на 8и-битные значения? Небольшая процессинговая компания Authorize.Net использовала знаковое целое, когда дела пошли в гору им пришлось рассылать уведомления всем своим заказчикам. Повезло тем, кто трактовал все данные от компании как строка, тех изменения не затронули, но остальным пришлось попотеть так или иначе. Думаете ваше приложение никогда не будет столь велико и популярно? Я надеюсь, что все мои проекты будут удачны, так что планирую соответствующе.

Третьей причиной и самой явной и серьезной для меня является то, что в 100% распределенных системах желательно полностью избежать единой точки выдачи уникальных ключей. Оперируя GUID вместо целых чисел, можно переложить генерирование уникальных идентификаторов на приложение. Сохранять объекты в базу сразу, не выполняя дополнительных действий по синхронизации уникальных ключей генерируемых базой – что освобождает немалые ресурсы. Такой подход так же позволяет использовать «временно соединяемые системы».

В четвертых, использование GUID добавляет в приложение «безопасности по незнанию». Примерно два года назад мы заказали несколько машин у Dell. Когда дело дошло до финальной страницы, я заметил, что параметр в URL был в духе «&orderId;=12345678». Ради интереса я вычел единицу и обновил страницу. Не ожидал, но все же я увидел чей-то заказ, который был совершен несколько минут назад. Там присутствовали все реквизиты, адреса, и прочая «чувствительная» информация. На мгновение мелькнула мысль, что можно написать скрипт и собрать информацию обо всех заказчиках, на сайте не было нормальной авторизации. Конечно, это глупая ошибка, которую вы, скорее всего, избежите, но использование GUID дает дополнительную защиту.

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

Итого

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

 

Hard’n’heavy!

 

Via http://blog.jonathanoliver.com/2009/01/integers-vs-guids-and-natural-vs-surrogate-keys/

2 комментарий на “Целые vs GUID vs Естественные и Суррогатные ключи

  1. Да, для распределённых систем такой подход сильно облегчает задачу генерирования уникального ключа на разных узлах системы, даже полностью решает её. Но для небольшого приложения, где каждый байт на счету (хотя, наверно, это уже большая редкость) всё же лучше юзать целочисленные ключи. + это немного быстрее, чем генерировать сложный гуид. Опять же, спор можно продолжать и продолжать, но универсального решения, которое оптимально для всех систем одновременно, не будет.

  2. у меня была задача сделать репликацию слиянием двух баз данных. В качестве первичных ключей использовал целый числа. Решил проблему разделением диапазона значений генерируемых ключей. На мой взгляд такой подход исключает вероятность совпадения первичных ключей в отличии от GUID… Я придерживаюсь правила: Если есть вероятность чего то случиться, то оно обязательно случиться.

Оставить комментарий