Про GUID-ы и INT-ы

Последнее время я часто думаю о том, чтобы начать использовать в новых таблицах в качестве суррогатного первичного ключа не привычные большинству целые числа INT IDENTITY (1,2,3, …), а глобальные уникальные идентификаторы GUID (5e09962e-1de5-48a6-a71e-fb9c5ec58b01, 5aeae3ec-fccf-4739-bde6-09d3577fa121, …). Большинств коллег с некоторым недопониманием и вялым сопротивлением отнеслись к моей инициативе, поэтому я решил написать этот пост в котором расскажу о плюсах данного подхода и совсем немного (я же всё-таки предвзят) упомяну о минусах.

Начну с минусов

Даже визуально заметно, что GUID гораздо «длиннее» чем число. Это приводит сразу к двум проблемам.

  • Во-первых, GUID-ы тяжело использовать человеку, т.е. просто написать список идентификаторов в секции WHERE по памяти уже не удастся, придётся копировать.
  • Во-вторых, GUID действительно занимает в четыре раза больше места, чем INT. При простом хранении данных в базе это не так уж страшно, но может стать проблемой, когда возникнет необходимость добавить очередное поле в индекс. Мой, пусть не очень большой, но всё-таки опыт подсказывает, что «пухлые» индексы возникают в основном в ненормализованных БД, так что вторая проблема на самом деле вызвана не столько GUID-ами как таковыми, сколько неправильными архитектурными решениями.
  • В-третьих, почти все коллеги обращают внимание на тот факт, что таблицу нельзя упорядочить по столбцу с GUID-ам, получив тем самым псевдовремя. На это замечание у меня сразу два возражения: во-первых, псевдовремя порочно по сути своей (всегда можно использовать настоящее время), во-вторых, столбцу с псевдоключом можно задать значение по умолчанию равное NEWSEQUENTIALID(), тогда до перезагрузки сервера значения в данном столбце таблицы будут возрастать.

Теперь давайте о плюсах

GUID не зря называют глобальным уникальным идентификатором. Его значения статистически уникальны.

  • Т.е. мы легко можем создавать новые идентификаторы без непосредственного доступа к базе (например, в мобильном приложении) и быть уверенными, что их вставка не приведёт к конфликтам (при этом, конечно, нельзя использовать полученные значения для вставки в столбец со значением по умолчанию NEWSEQUENTIALID, но на практике это нужно относительно редко).
  • Это же свойство статистической уникальности приводит к тому, что каждый новый GUID выглядит случайным, что автоматически усложняет и делает практически невозможным подбор корректных значений в URL-адресах, используемых для подтверждения регистрации, восстановлений пароля и т.п.
  • Третьим несомненным плюсом является огромное (можно для простоты считать, что бесконечное) количество различных возможных значений. Всем кажется, что типа INT хватит всем и навсегда. Я тоже так думал, ровно до того момента, пока не заметил, как коллеги три дня искали ошибку, вызванную тем, что в таблице банально закончились новые идентификаторы.
  • Ну и наконец, ещё одно полезное свойство глобальной уникальности GUID-ов, которое окончательно склонило меня к их использованию. Если у нас в таблице t2 есть два столбца t2ID, являющийся суррогатным ключом таблицы t2, и t1ID, ссылающийся на суррогатный ключ таблицы t1, то вполне можно допустить обидную опечатку, написав в предикате соединения t2ID вместо t1ID или наоборот (я, например, часто так ошибаюсь). В случае использования INT-ов, такой очевидно ошибочный JOIN скорее всего даст на выходе непустой набор строк. Невнимательный программист не только не заметит ошибку, но и вставит полученный набор данных в какую-нибудь другую таблицу. Результат предсказать довольно тяжело, но я точно могу сказать, что ни к чему хорошему это не приведёт. Если же в столбцах хранились GUID, даже самый невнимательный программист удивится, почему простой JOIN даёт пустой набор строк. В результате в коде будет на одну ошибку меньше. По-моему, ошибку всегда лучше предотвратить, если есть такая возможность.

Как всегда, если у вас есть замечания, предложения, или вы просто хотите поспорить — милости прошу в комментарии.

P.S. Уже после публикации мне подсказали ещё несколько плюсов:

  • Говорят, что GUID-ы также незаменимы при репликации БД, но тут у меня опыта нет, поэтому утверждать не берусь.
  • Часто бывают ситуации, когда используются значения по умолчанию. Например, каждой новой заявке в техподдрежку по умолчанию присваивается статус «Ожидает назначения специалисту» (ID этого статуса 0). Вся система стабильно работает, но ровно до того момента, пока руководство не решит, что необходим новый статус по умолчанию (например, «Заявка создана» с ID 4). На первый взгляд доработать систему просто, надо в параметрах по умолчанию заменить 0 на 4, но довольно быстро вы поймёте, что значений 0 у вас в базе огромное количество, и понять, какое из них относится к заявкам, а какое к каким-то другим бизнес-процессам очень тяжело.

Про GUID-ы и INT-ы: 3 комментария

  1. yandex.ru Миша 1

    Да, особенно актуально генерить айдишник на сервере приложений, когда серверов много используется для балансировки. Меньше запросов к базе существенно. Давно хочу попросить спецов НТ провести для сравнения производительности того и другого варианта. Сейчас в моей основной системе guid в качестве PK. Поначалу недоумевал, поэтому собирал подобные размышления в голове.

    В пользу числовых идентификаторов еще скажу упорядоченность хранения в индексах. Это уменьшает количество чтений при некоторых запросах.

    1. GORKOFF Автор записи

      А я сейчас делаю разработку в которой пытаюсь в принципе отказаться от суррогатных ключей, никаких INT-ов, никаких GUID-ов, никаких NULL-ов. Всё, как учил Кодд и Дейт.
      Система пока в стадии тестирования, но уже сейчас очевидно, что такой подход помог избежать множества ошибок-опечаток.

  2. Уведомление: Про суррогатные ключи | GORKOFF

Добавить комментарий