UUIDv7 - это звезда первой величины среди типов идентификаторов и ключей
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)