MySQL и PostgreSQL

Автоинкремент и UUID в MySQL и PostgreSQL: как генерировать уникальные ID

MySQL использует свойство столбца AUTO_INCREMENT, а PostgreSQL — последовательности (sequence), обёрнутые в SERIAL или современный синтаксис GENERATED AS IDENTITY. Оба способа решают одну задачу — выдать каждой новой строке уникальный числовой номер, но устроены по-разному, и от этого зависит поведение базы при нагрузках, репликации и распределённых системах.

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

Почти в каждой таблице базы данных есть первичный ключ — поле, которое однозначно определяет строку. Проще всего сделать его числом, которое увеличивается на единицу при каждой новой записи: первый пользователь получает id 1, второй — id 2 и так далее. Это удобно для человека, компактно для базы и быстро для индексов. Именно поэтому автоинкрементные столбцы стали стандартом де-факто в реляционных базах.

Как это работает в MySQL?

В MySQL всё просто: при создании столбца достаточно указать свойство AUTO_INCREMENT, и база сама будет подставлять следующее число при каждой вставке. Счётчик хранится вместе с таблицей и зависит от движка хранения — например, InnoDB может вести себя чуть иначе после перезапуска сервера, чем MyISAM, из-за особенностей кэширования счётчика в памяти.

Плюс такого подхода — минимум телодвижений для разработчика: один атрибут в определении таблицы, и всё работает. Минус — счётчик жёстко привязан к конкретной таблице, и им сложнее гибко управлять, если нужно, например, использовать одну последовательность для нескольких таблиц.

А как устроен автоинкремент в PostgreSQL?

PostgreSQL исторически использует другой механизм — последовательности (sequence). Это отдельный объект базы данных, который умеет генерировать числа по заданным правилам: с каким шагом расти, с какого значения начинать, что делать при достижении максимума. Тип SERIAL, который часто используют новички, на самом деле не настоящий тип данных, а просто удобная обёртка: PostgreSQL создаёт под капотом последовательность и привязывает её к столбцу через DEFAULT.

Начиная с десятой версии в PostgreSQL появился более стандартный синтаксис — GENERATED AS IDENTITY, который соответствует стандарту SQL и ведёт себя предсказуемее старого SERIAL. Он лучше защищён от случайных ошибок, когда кто-то вручную вставляет значение в столбец и сбивает счётчик.

В чём принципиальная разница между подходами?

Главное отличие в том, что последовательность в PostgreSQL — это самостоятельный, независимый от таблицы объект. Её можно использовать для нескольких таблиц одновременно, менять шаг увеличения, откатывать вручную или создавать циклические счётчики. AUTO_INCREMENT в MySQL — это свойство конкретного столбца, более простое, но и менее гибкое.

Есть и практическая разница в поведении при откате транзакций. И в MySQL, и в PostgreSQL счётчик увеличивается независимо от того, успешна транзакция или нет — то есть в номерах id могут появляться «дыры», если вставка не удалась. Это нормальное поведение обеих систем, а не баг, и рассчитывать на строго последовательные номера без пропусков не стоит ни в одной из СУБД.

Клауд АйСи — цифровые сервисы для бизнеса в одном кабинете: 1С в облаке с ежедневными резервными копиями, создание сайтов под ключ, Умный онлайн-чат и проверка контрагентов.
Ваши данные под защитой. Есть бесплатные тарифы навсегда.

Что не так с обычными числовыми ID?

Числовой автоинкремент отлично работает, пока у вас один сервер базы данных. Но как только проект растёт и появляется несколько независимых баз — например, при горизонтальном масштабировании, работе нескольких филиалов офлайн или синхронизации мобильных приложений — возникает проблема: разные базы могут сгенерировать одинаковый номер для разных записей. Числа предсказуемы и легко угадываются, что не всегда хорошо с точки зрения безопасности: по последовательным id в URL можно попытаться перебрать чужие заказы или профили.

Здесь на сцену выходит UUID — в чём его смысл?

UUID — это универсальный уникальный идентификатор, длинная строка из букв и цифр, вероятность случайного совпадения которой практически равна нулю. Такие идентификаторы можно генерировать где угодно — хоть на сервере, хоть на клиентском устройстве — и они не столкнутся друг с другом, даже если создаются одновременно в разных системах без связи между собой. Это особенно ценно при распределённой архитектуре и микросервисах.

И MySQL, и PostgreSQL умеют работать с UUID: PostgreSQL имеет для него отдельный тип данных, а в MySQL UUID обычно хранят как строку или бинарное значение. Расплата за удобство — размер: UUID занимает заметно больше места, чем обычное число, и индексы по нему работают медленнее из-за отсутствия естественной сортируемости по возрастанию.

Так что выбрать для своего проекта?

Если проект стандартный — сайт, интернет-магазин, CRM на одной базе данных — числового автоинкремента вполне достаточно, и лишние сложности с UUID себя не окупят. Это проще, компактнее и быстрее для типичных нагрузок.

  • Выбирайте автоинкремент или SERIAL/IDENTITY, если у вас одна база данных и не планируется распределённая архитектура;
  • Используйте UUID, если данные создаются в разных независимых системах и должны потом объединяться без конфликтов;
  • Рассмотрите UUID также там, где важно скрыть от посторонних глаз реальное количество записей или порядок их создания;
  • Для PostgreSQL по возможности предпочитайте современный синтаксис GENERATED AS IDENTITY вместо устаревающего SERIAL.

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

Можно ли не разбираться во всём этом самому?

Выбор между автоинкрементом и UUID, настройка последовательностей, обновление версий PostgreSQL с переходом на новый синтаксис — всё это требует внимания администратора базы данных. Если такого специалиста в штате нет, разумно передать эксплуатацию базы на аутсорс. Сервис «Клауд АйСи» предоставляет готовые к работе облачные базы данных MySQL и PostgreSQL с грамотными настройками по умолчанию, автоматическим резервным копированием и поддержкой — можно сосредоточиться на логике приложения, а не на тонкостях генерации идентификаторов.

1С в облаке с резервным копированием

Зарегистрируйтесь и подключите облачную 1С — ежедневные бэкапы и обновления уже включены.

Частые вопросы

Может ли AUTO_INCREMENT в MySQL пропускать номера?

Да, это нормальное поведение. Если транзакция с вставкой строки откатывается или происходит сбой, зарезервированный номер не возвращается в оборот, и в последовательности id появляется пропуск.

Чем SERIAL отличается от GENERATED AS IDENTITY в PostgreSQL?

SERIAL — это старая обёртка над последовательностью, которая создаёт значение по умолчанию через DEFAULT и позволяет случайно перезаписать его вручную. IDENTITY — более новый, стандартный подход, который лучше защищён от подобных ошибок и ближе к стандарту SQL.

Стоит ли использовать UUID вместо чисел для всех таблиц подряд?

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

Можно ли использовать одну последовательность для нескольких таблиц в PostgreSQL?

Да, поскольку последовательность в PostgreSQL — самостоятельный объект базы данных, её можно привязать сразу к нескольким столбцам в разных таблицах, если это нужно по логике проекта.