November 6, 2022

Зачем UUID?

Что выбрать в качестве идентификатора для моей сущности?

Auto increment, предоставляемый БД – вот, что первое приходит на ум.

Но действительно ли он подходит для наших задач? В большинстве случае да, но не всегда, давайте изучим вопрос.

Что за UUID?

UUID - Universally Unique IDentifier - универсальный уникальный идентификатор, так же вы можете встретить аббревиатуру GUID - Globally Unique IDentifier, что в принципе то же самое. Предоставляет собой 128-битное число и записывается как строка в формате пяти групп, разделенных дефисами 8-4-4-4-12:

1
84f3ee04-96f1-4c9c-993c-3ca3bf0907d8

Есть несколько версий UUID, но самая быстрая и надежная, и как ни странно, часто используемая - Версия 4, когда UUID генерируется случайным образом.

Уникальность

С уникальностью UUID справляется успешно, хотя это можно подвергнуть сомнению, особенно 4 версию, потому что генерируя UUID случайным образом, всегда есть некоторый шанс того, что последовательность совпадет несколько раз, но эти шансы очень и очень малы, если вы сгенерируете 10 триллионов UUID, то шанс появления двух одинаковых значений равна 0,00000006%. Вероятность коллизии есть, но UUID достаточно уникален для практических целей. Вероятность коллизии хорошо раскрывает следующая статья.

Применение

Собственно, а зачем?

Распределенные системы

Основное назначение UUID - это дать возможность уникально идентифицировать объекты информации в распределенных системах, без центра координации. Примером распределенных систем является ПО построенное по микросервисной архитектуре, представьте, у нас есть микросервисы Каталог, Корзина, Заказ, Доставка, Отзыв и Статистика где используется такая сущность как товар. Возьмем, к примеру, товар Кофемашина с UUID 84f3ee04-96f1-4c9c-993c-3ca3bf0907d8, и данная Кофемашина имеет один и тот же идентификатор во всех микросервисах, работая в рамках их контекстов, мы всегда понимаем, что мы работаем с одним и тем же товаром. Это позволяет нам легко реагировать на изменения товара или взаимодействие с ним в рамках остальных микросервисов. Упрощает работу с журналом, потому что в логах всех системы, мы будем искать только по одному идентификатору.

Архитектура приложения

Опираясь на принципы чистой архитектуры, наша бизнес логика ничего не должна знать о внешнем мире, в том числе и БД. Но в рамках бизнес логики мы все так же продолжаем выполнять операции над сущностями, а самое главное создавать их. А значит, нам необходимо присваивать им идентификаторы, в случае с auto increment мы зависимы от БД, мы должны сходить в БД, сохранить сущность, и получить ид для дальнейшей работы с сущностью. Так мы нарушаем принципы чистой архитектуры и теряем возможность асинхронность.

Хотя можно реализовать сервис выдачи идентификаторов, но решение весьма трудозатратное.

Асинхронность

В случае с PHP, в данном контексте, под асинхронностью понимается способ выполнения бизнес-процесса в фоне, не на хите. Такая ситуации возникает, когда мы генерируем domain events, и записываем их в брокер сообщений, чтобы обработать их позже. В данном случае, наш сущность ещё может быть не сохранена в БД, чтобы получить auto increment, а так же наши события имеют собственный идентификатор, получать который через БД весьма накладно.

Безопасность

Ещё один плюс в сторону UUID, который можно встретить, изучая вопрос о преимуществах UUID. Он строится на том, что запомнить id 45323 проще, чем 47a15751-588c-4b1a-9b32-822061851a3b, а значит подглядывающий в ваш монитор не сможет украсть его и получить информацию по нему. Пример с монитором очень неправдоподобный, но суть он передает, и подход действительно полезен, например, в системе-агрегатор расчета кредитных условий по заполненной форме пользователем. Пользователь отправляет форму, форма сохраняется и пользователь получает id. Backend в фоне рассылает форму в API банков и получает предложения для клиента. В это время frontend запрашивает по API статус заявки, указывая id, и если есть новые предложения, то выводит их, и продолжает опрашивать backend, до тех пор, пока backend не скажет, что ответы от всех банка получены. Т.е. у нас в такой системе есть API метод с возможностью получить заявку и предложения по ней по id. Если мы будем использовать integer auto increment, то мы сможем получить доступ ко всем расчетам, начиная перебор от 1 до 4294967295 (или до 18446744073709551615, если bigint). Ко всему, можно узнать кол-во заявок всего в системе, и можно замерить сколько заявок в день/неделю/месяц делают пользователи на данном сервисе. Если использовать в качестве id UUID такой перебор невозможен, так как его генерация не предсказуема.

Ну всё, используем только UUID ?

У всего есть свои плюсы и минусы. Минусы UUID в том, что он много занимает место (16 байт), по сравнению с числовым идентификатором, в некоторых случаях использования UUID может ухудшить производительность БД, но насколько это важно? Мнения расходятся на этот счет.

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

Выбор за вами 😉

Ссылки