UUIDv7 — это звезда первой величины среди типов идентификаторов и ключей
В конце сентября 2025 года вышла СУБД PostgreSQL 18. Она получила долгожданную встроенную функцию uuidv7(). Функция uuidv7() генерирует согласно международному стандарту RFC 9562 идентификаторы типа UUID версии 7 (UUIDv7) с бинарным типом данных uuid, рекомендованные и используемые в качестве первичных ключей. При необходимости таймстемп с часовым поясом может быть извлечен из них с помощью функции uuid_extract_timestamp().
UUIDv7 сочетает в себе глобальную уникальность первичных ключей, пренебрежимо малую вероятность коллизий (недопустимых случайных совпадений) и упорядоченность по моменту времени генерации. При этом не используются централизованная координация вычислений и MAC-адреса. Риск коллизий не выше, чем у прежде самого популярного (случайного) типа UUID версии 4.
Благодаря упорядоченности по моменту времени генерации UUIDv7 обеспечивают гораздо большую производительность и меньшее потребление дискового пространства для индексов по сравнению с UUIDv4. Старшие биты идентификаторов UUIDv7 могут использоваться в качестве ключа секционирования (partition key).
UUIDv7 обеспечивают такую же производительность CRUD-операций БД, как при использовании автоинкремента (типа serial и его современного аналога GENERATED … AS IDENTITY). Время генерации идентификатора UUIDv7 приблизительно в тысячу раз меньше времени вставки записи, поэтому темп генерации UUIDv7 не влияет на производительность БД.
Использование UUIDv7 позволяет избавиться от фундаментальных недостатков автоинкремента:
сложность слияния данных с одинаковыми ключами из разных таблиц БД
необходимость генерации новых ключей и их синхронизации с ключами из источника данных при экспорте и импорте данных и при параллельной генерации записей несколькими процессами (микросервисами)
необходимость в промежуточных таблицах при слиянии данных
возможные ошибки из-за коллизии ключей при слиянии данных
раскрытие количества записей в таблице БД
легкость подбора действительного ключа методом последовательного перебора
невозможность полнотекстового поиска по идентификатору в интернете
В отличие от сторонних расширений PostgreSQL для генерации UUIDv7 и от генерации идентификаторов UUIDv7 в приложениях, встроенная функция обеспечивает простоту использования и монотонность (возрастание) идентификаторов, генерируемых в интервалах короче миллисекунды. Эта монотонность в течение миллисекунды нужна для поиска причин ошибок, пагинации по ключу (keyset pagination), поиска в логах, использования в БД временных рядов и т.п.
Особенности реализации функции uuidv7() в PostgreSQL 18:
имеет дополнительный 12-битный субмиллисекундный сегмент таймстемпа с точностью около 250 наносекунд (на macOS — 10-битный с точностью около 1 микросекунды)
не использует ни мьютекс, потенциально ограничивающий производительность, ни атомарные переменные
в критических ситуациях использует таймстемп в качестве счетчика
содержит опциональный параметр (типа interval) смещения даты и времени вперед или назад с защитой таймстемпа от переполнения аналогично кольцевому буферу
требует применения криптографически стойкого генератора псевдослучайных чисел (CSPRNG)
Использование таймстемпа в качестве счетчика в критических ситуациях гарантирует монотонность и уникальность при генерации в одном процессе даже при временной недоступности системных часов или их откате назад, а также при сколь угодно высоких темпах генерации. При параллельной генерации в нескольких процессах (микросервисах) как правило обеспечивается монотонность — за счет дополнительного 12-битного субмиллисекундного сегмента таймстемпа. Незначительные нарушения монотонности не влияют на производительность БД.
Смещение значения таймстемпа с помощью параметра позволяет замаскировать фактическую дату создания записи, предотвращает конфликты блокировок при параллельной генерации UUIDv7 в нескольких процессах, а также улучшает монотонность при генерации на удаленных клиентах. В случае смещении значения таймстемпа с помощью параметра при генерации UUIDv7 функция uuid_extract_timestamp() будет выдавать смещенное значение даты и времени.
Теоретически смещение значения таймстемпа позволяет также гарантировать уникальность при генерации в разных процессах (микросервисах). Но в этом нет реальной необходимости. Ведь удлиненный таймстемп вместе с длинным случайным сегментом обеспечивают пренебрежимо малую вероятность коллизии идентификаторов UUIDv7.
Пример использования:
SELECT uuidv7(); — Create clients table with UUIDv7 as primary key with masked timestamp (5500 years + 12.5 hours forward) CREATE TABLE clients ( id uuid DEFAULT uuidv7(INTERVAL ‘5500 years 12 hours 30 minutes’) PRIMARY KEY, name text NOT NULL, email text, created_at timestamptz DEFAULT CURRENT_TIMESTAMP ); — Insert clients. Let the DEFAULT value generate the UUID INSERT INTO clients (name, email) VALUES (‘John Smith’, ‘john.smith@example.com’), (‘Emma Watson’, ’emma.watson@example.com’), (‘Michael Brown’, ‘michael.brown@example.com’); — If you need a UUID for a past date, the interval should be negative INSERT INTO clients (id, name, email) VALUES (uuidv7(INTERVAL ‘-11 years -5 hours -44 minutes’), ‘James Anderson’, ‘james.anderson@example.com’), (uuidv7(), ‘Olivia Parker’, ‘olivia.parker@example.com’); — Verify the masked timestamp SELECT id, name, email, created_at as actual_creation_time, uuid_extract_timestamp(id) as masked_uuid_timestamp, uuid_extract_timestamp(id) — created_at as timestamp_shift FROM clients ORDER BY id;
Английский вариант этой новости (English version of this news)
Источник: habr.com