Идентификаторы в БД: INT vs UUID

от автора

Привет! Меня зовут Женя, я бэкенд-разработчик, и в этом посте хотела бы кратко обсудить плюсы и минусы разных вариантов идентификаторов в базе данных.

Sequential INT

Думаю, все сталкивались с этим вариантом, когда id новых сущностей генерирует база при вставке, и они получают монотонно возрастающие номера:
id = 1, id = 2, id = 3…

Плюсы:

➕ удобно читать и воспринимать человеку
Например, это удобно, когда разбираем какие-то баги, смотрим логи.

➕ просто сделать

➕ можно видеть порядок добавления записей без поля createDate
Не очень частный кейс, но может быть актуально, например, для справочников.

➕ благодаря монотонному возрастанию можно использовать для keyset пагинации без offset.

Keyset — это способ реализации пагинации, при котором для получения данных отдельной страницы вместо добавления в запрос сдвига offset:

select name from users order by id limit 10 offset 50 

используется условие:

select name from users where id > x order by id limit 10

где x — это последнее значение с прошлой страницы.

➕ использует всего 64 бита (для bigint)

➕ при добавлении новых записей в БД индекс обновляется быстро благодаря последовательности ключей

➕ запросы выполняются быстрее, поскольку благодаря равномерному индексу активно используется кэш базы данных, в котором лежат стабильные части «горячих» данных — последний день, месяц

Минусы:

➖ основной минус проявляется при шардинге: если у нас много шардов базы, то при стандартном подходе сложно сохранить уникальность числовых идентификаторов.
Если каждый шард базы генерирует свои идентификаторы, то они будут пересекаться с другими шардами.
Решением может стать отдельный сервис для централизованной генерации айдишников, но у него тоже есть свои минусы (единая точка отказа, снижение скорости ответа в случае географически распределенных серверов).

➖ если числовой айдишник показывается на фронте, то из этой информации кто-то может сделать выводы, вредные для репутации или безопасности продукта.

Например, мы сделали свой новый продукт, написали на главной странице «Нам доверяют более 1000 пользователей!». Новый пользователь регистрируется на сайте, фронт отправляет запрос:

POST /users {    "name": "Alex",    "age": 25  }

и получает ответ:

{   "id": 8,   "name": "Alex",   "age": 25 }
Когда возвращаешь в ответе числовой id

Когда возвращаешь в ответе числовой id

В некоторых источниках можно встретить упоминание, что последовательно возрастающие числовые идентификаторы небезопасны, так как злоумышленник может угадать/перебрать идентификаторы и получить доступ к чужим данным. Однако здесь дело не только в типе айдишника. Злоумышленник может подсмотреть его или получить от какого-то пользователя. Независимо от типа идентификатора, на бэке всегда нужно проверять права пользователя. Может ли этот пользователь взаимодействовать с этой сущностью, может ли он ее читать, редактировать, удалять.

История из личного опыта. После сдачи дома застройщик не торопился присылать данные обмеров квартир, и люди в чате ЖК забеспокоились. Один из участников чата нашел на сайте застройщика нужный файл PDF с id в адресе файла и рассказал в чате. Путём подбора id в URL все смогли скачать себе PDF своих (и чужих) квартир. Не то чтобы обмеры квартир — это секретная информация, но вот так мы добыли их до официальной рассылки застройщика.

UUIDv4

UUID (Universally Unique Identifiers) записываются в виде последовательности шестнадцатеричных цифр в нижнем регистре, разделённых знаками минуса на несколько групп. Например:

5b3201b6-b8e6-4544-96a7-ac046a734f17

UUID генерируется специальным алгоритмом, практически гарантирующим, что этим же алгоритмом оно не будет получено больше нигде в мире.

Плюсы и минусы идентификатора типа UUID во многом обратны таковым у числовых идентификаторов.

Плюсы:

➕ можно генерировать в коде приложения до записи в БД

➕ удобно в случае шардинга БД: у всех записей свои уникальные айдишники (можно перешардировать, можно смерджить две базы) — это основной плюс такого вида id

➕ при отображении на фронте не даёт пользователю информации о количестве записей (кейс с безопасностью остается, так как пользователь может переслать ссылку со своим uuid— всегда проверяйте права)

Минусы:

➖ неудобно для восприятия человеком

➖ генерация занимает дольше, если это делает БД (не играет роли, если генерирует приложение)

➖ занимает больше места — 128 бит (в большинстве случаев некритично)

➖ не виден порядок добавления записей без поля createDate

➖ не сделать keyset пагинацию по id

Индекс по UUIDv4 является неравномерным — это значит что соседние значения в индексе, например
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' и
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaab'
будут указывать на данные, которые расположены физически далеко, в разных блоках таблицы. В равномерном же индексе соседние значения указывают на один и тот же блок физических данных.

Из неравномерности индекса по UUIDv4 вытекают еще 2 минуса:

➖ при вставке в БД сильно перестраивается индекс (происходит чтение и запись в разные страницы индекса), что замедляет вставку

➖ запросы на чтение выполняются дольше, так как мало используется буферный кэш: нет «горячих частей», таких, как компактное хранение всех id текущего дня в ограниченном количестве блоков индекса.

UUIDv7

Для решения проблем, связанных с отсутствием последовательности в UUIDv4, были придуман новый формат UUIDv7. Например:

0193971d-b693-7853-bc89-7a13bda9a033

Он занимает те же 128 бит, но первые 36 бит занимает метка времени. На сайте uuid7.com можно проверить валидность UUIDv7 и определить дату генерации.

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

Про UUIDv7 на Хабре уже были подробные статьи, например: как устроен UUIDv7, спецификация UUIDv7 по RFC9562.

Кроме UUIDv7, который является частью стандарта, есть и другие UUID-подобные идентификаторы с метками времени: ULID, Snowflake ID и др.


В итоге, нельзя сказать, что какой-то тип идентификатора является «серебряной пулей». Как обычно, нужно отталкиваться от задачи и требований. При этом, если вы выбрали для нового проекта UUID, то очень рекомендую рассмотреть вариант v7 или другую реализацию с сортировкой. Надеюсь, что резюме по плюсам и минусам различных подходов вам пригодится!

У меня есть маленький ламповый блог в телеграм, где делаю разборы книг, пишу про разработку, архитектуру и немного про свой опыт. Буду рада, если вас он заинтересует.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Какой тип id используется у вас на проекте?

44% INT99
28.89% UUIDv465
22.22% UUIDv7 или другой UUID с меткой времени50
4.89% Другой тип11

Проголосовали 225 пользователей. Воздержались 20 пользователей.

ссылка на оригинал статьи https://habr.com/ru/articles/864188/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *