Совсем скоро (в конце сентября) выйдет PostgreSQL 18. Релиз готовит важные обновления — от асинхронного I/O до EXPLAIN с показателями CPU и WAL.
Довольно громкая новинка — нативная поддержка UUIDv7, нового стандарта уникальных идентификаторов, идеально подходящих для B-tree индексов.
В новом переводе от команды Spring АйО рассказывается, почему это важно, как работает UUIDv7 и чем он лучше UUIDv4 для современных распределённых систем.
UUIDv7 в PostgreSQL 18
PostgreSQL 18 уже на подходе — началось бета-тестирование. Среди множества улучшений в этом релизе — поддержка UUIDv7
. Это вариант UUID
на основе временной метки, который хорошо работает с B-tree индексами. В этой статье мы обсудим, что такое UUID
, чем полезен UUIDv7
и как вы можете использовать его в PostgreSQL.
PostgreSQL 18
Beta-версия PostgreSQL 18 (beta 1) вышла относительно недавно. Релиз включает в себя множество новых фич, улучшений и исправлений. Как обычно, сообществу предлагается протестировать версию и сообщить о найденных багах — цель состоит в том, чтобы выпустить стабильный и качественный релиз в сентябре.
Среди ключевых нововведений:
Асинхронный I/O (с использованием
io_uring
) — ускорение последовательных сканирований (seqscan
) и операций VACUUM в 2–3 раза-
“Пропускное сканирование” в мультиколоночных B-tree индексах и более умная оптимизация OR/IN.
Комментарий от эксперта Spring АйО, Михаила Поливахи: Речь про так называемый «skip scan». Это оптимизация в рамках Postgres 18, которая применяется в определенных ситуациях, когда задействован мультиколоночный btree индекс. Пока устоявшегося в сети термина для перевода на русский язык я по крайней мере не знаю, поэтому, перевели как «Пропускное сканирование».
Оптимизация на самом деле крайне важная, но она не является целью статьи. Позже, если будут просьбы, мы про неё расскажем в деталях, и как Вы можете ей пользоваться
Сохранение статистики планировщика при major обновлениях
Функции для работы с
UUIDv7
Виртуальные вычисляемые столбцы
Вход через OAuth и предупреждение об окончании поддержки
md5
EXPLAIN ANALYZE теперь показывает I/O, CPU, WAL
Временные ограничения, LIKE для недетерминированных правил локализации (речь про collation), casefolding
Новая версия протокола обмена данными: 3.2 (первая с 2003 года!)
Хотя функция uuidv7()
— не самая «громкая» фича релиза (это звание скорее достаётся асинхронному I/O), она, пожалуй, самая ожидаемая. Её чуть было не добавили ещё в версии 17, и многие пользователи были разочарованы, что этого не произошло.
Что такое UUID и зачем он нужен?
UUID
— это 128-битные значения, используемые как идентификаторы для различных объектов: от транзакций до компаний. Они спроектированы так, чтобы быть уникальными в пространстве и времени, и могут генерироваться с высокой скоростью. Основное преимущество в том, что разработчикам систем нет необходимости иметь централизованный сервис для их генерации.
Традиционно в реляционных базах данных для генерации уникальных идентификаторов использовались автоинкрементные типы (например, SERIAL
или IDENTITY
). Это работает эффективно на одной машине (хотя даже в этом случае есть недостатки), но при масштабировании возникает необходимость в генерации уникальных идентификаторов на всех узлах.
UUID
удобно использовать в качестве первичных ключей в базах данных в следующих типичных сценариях:
Генерация уникальных идентификаторов в распределённых БД
Хотя многие распределённые СУБД поддерживают автоинкрементные (identity
) столбцы, они имеют ограничения и проблемы с производительностью.
Непредсказуемые публичные идентификаторы
При правильной генерации UUID
нельзя угадать, предсказать или использовать для получения информации о системе. Если, например, в качестве идентификатора клиента используется автоинкремент, злоумышленник может сканировать все существующие ID, и сможет предсказать следующий идентификатор, или, например, оценить число ваших клиентов и т.п.
Позволить клиентам генерировать идентификаторы самостоятельно
Использование UUID даёт возможность клиентам генерировать идентификаторы без координации с сервером. Это полезно в мобильных приложениях и serverless архитектурах, где желательно минимизировать сетевое взаимодействие.
Благодаря этим преимуществам UUID
широко применяется в качестве первичных ключей. Однако есть и три распространённые проблемы при использовании UUID
в базах данных:
Сортировка:
UUID
не упорядочены по значению в каком-либо полезном смысле.Локальность в индексах: Новые
UUID
генерируются случайным образом, а значит, вставки происходят в произвольные места индекса. Это приводит к раздутию индекса и проблемам с производительностью.
Комментарий от эксперта Spring АйО, Михаила Поливахи: На эту тему, да и вообще в целом по использованию Btree индексов я рекомендую посмотреть доклад Владимира Ситникова: https://www.youtube.com/watch?v=y-Wtyvme4gEРазмер:
UUID
— это 128-битное значение. Многие разработчики по умолчанию используют INT (32-бит) или BIGINT (64-бит) для первичных ключей. Для таблиц с большим числом очень маленьких записей это может быть ощутимым оверхедом.
Далее мы посмотрим, как UUIDv7
решает две из трёх этих проблем.
Размер UUID
может стать проблемой при ограниченном дисковом пространстве или при наличии ограничений по сетевой пропускной способности, но стоит отметить, что современные процессоры умеют сравнивать 128-битные значения за одну инструкцию (например, CMEQ — часть SIMD-инструкций), поэтому операции с UUID
в базах данных хорошо оптимизированы. Главное — использовать бинарное представление UUID
(тип UUID
) как в базе, так и в приложении, а не строковое.
Зачем нужен UUIDv7?
UUID
впервые был стандартизирован в RFC 4122 в 2005 году. Этот документ определил пять вариантов/версий UUID
, из которых наиболее широко используются варианты 1 и 4. Позже спецификация была обновлена — в мае 2024 года был опубликован RFC 9562, который добавил версии 6–8 (хотя первая публичная черновая версия появилась ещё в 2020 году). С днём рождения, RFC 9562 и UUIDv7
!
Чтобы обосновать необходимость обновления спецификации, RFC 9562 рассматривает популярный сценарий использования UUID в качестве первичных ключей в базах данных:
«Одна из областей, где UUID
получил широкое распространение, — это ключи баз данных ... но версии UUID
1–5, изначально определённые в [RFC4122], лишены некоторых важных свойств, например:
Версии UUID
, не упорядоченные по времени (такие как UUIDv4
, описанный в разделе 5.4), обладают плохой локальностью в индексах баз данных. Это означает, что последовательно созданные значения не находятся рядом друг с другом в индексе, что приводит к вставкам в случайные места. В результате возникают серьёзные проблемы с производительностью в структурах вроде B-tree и его вариациях».
Комментарий от эксперта Spring АйО, Михаила Поливахи
Вот тут авторы не поясняют, но я думаю, это важно. Почему вообще вставка в разные места индекса это проблема. Проблема в том, что из-за того, что UUIDv4
полностью рандомный, мы сталкиваемся с тем, что потенциально вставка, допустим, трёх строк со случайно сгенерированным UUIDv4
ведет к тому, что:
1. Скорее всего, прочитается 3 разные страницы памяти из heap-а для вставки + 3 потенциально дополнительных fsync системных вызова. Конечно, страницы индекса могут быть закешированы, скажет опытный разработчик, но тем не менее.
С UUIDv7
вставка будет почти точно в рамках одной странички, т.к. все 3 новые записи попадают в конец btree.
2. Как известно, b-tree самобалансирующийся. Это достигается путем сплита страниц и т.п. Из-за такой рандомизации UUIDv4 split страничек непредсказуемо происходит в произвольных местах индекса, чем потенциально может вызвать крупные ротации в рамках страниц индекса (сплит вызывает собой другой сплит и т.д.).
Само собой, UUIDv7
и вставка в конец индекса приводит к сплитам и мерджу страничек тоже, но их во-первых, предположительно, должно быть просто меньше.
В течение последних 10+ лет многие разработчики распределённых приложений и крупные вендоры пытались создать более подходящий, сортируемый по времени уникальный идентификатор для использования в базах данных. Это привело к появлению множества реализаций, решающих одну и ту же задачу по-разному.
RFC перечисляет 16 (!) различных нестандартных реализаций UUID
с разными компромиссами.
Хотя RFC вводит три новых варианта UUID
, действительно интересен только UUIDv7
. UUIDv6
добавлен лишь для обратной совместимости — в документе прямо говорится: «Системы, не зависящие от UUIDv1
, ДОЛЖНЫ использовать UUIDv7
». UUIDv8
предназначен для экспериментальных и специфичных для вендора расширений.
UUIDv7 решает сразу две ключевые проблемы:
сортировка значений
локальность вставок в индекс
Он использует временную метку в формате Unix Epoch в качестве старших 48 бит, а оставшиеся 74 бита отводятся под случайные значения (ещё несколько битов занимают версия UUID
и вариация/variant). Это делает UUID
одновременно упорядоченным по времени и уникальным. Стандарт также допускает добавление миллисекундной точности и/или аккуратно инкрементируемого счётчика для обеспечения порядка внутри одной секунды. В итоге UUIDv7
— отличный выбор для использования в качестве первичного ключа в базах данных: он уникален, сортируем и имеет хорошую локальность в индексах.
UUIDv7 в PostgreSQL 18
До выхода PostgreSQL 18 UUIDv7
не поддерживался на нативном уровне. Встроенная функция gen_random_uuid()
генерировала UUIDv4
, а популярное расширение uuid-ossp
, хоть и добавляло поддержку других вариантов UUID
, ограничивалось только теми, что были описаны в RFC 4122.
PostgreSQL 18 добавляет новую функцию: uuidv7()
, которая генерирует значения UUIDv7
. В реализации Postgres включена 12-битная субмиллисекундная составляющая сразу после основной временной метки (разрешено, но не обязательно по стандарту). Это гарантирует монотонность всех UUIDv7
, сгенерированных в рамках одного backend-процесса Postgres (одной сессии).
Комментарий от эксперта Spring АйО, Михаила Поливахи
Под backend-процессом имеется в виду процесс, который представляет собой fork процесса postmaster. Этот backend-процесс операционной системы обрабатывает ваш запрос.
Важно то, что подобных backend процессов, как их часто называют в разного рода литературе, может быть довольно много, и монотонный increase в UUIDv7
, как заявляет автор, гарантируется в рамках одного backend процесса. Имейте это в виду. Это важно.
Для единообразия также была добавлена функция uuidv4()
как алиас для gen_random_uuid()
.
Вызов uuidv7()
создаёт новый UUIDv7
, используя текущее время. Если необходимо сгенерировать UUIDv7
для другого времени, в функцию можно передать необязательный интервал.
Существующие функции Postgres для извлечения временной метки и версии из UUID
также были обновлены с учётом UUIDv7
. Вот пример использования новых функций:
postgres=# select uuidv7();
uuidv7
--------------------------------------
0196ea4a-6f32-7fd0-a9d9-9c815a0750cd
(1 row)
postgres=# select uuidv7(INTERVAL '1 day');
uuidv7
--------------------------------------
0196ef74-8d09-77b0-a84b-5301262f05ad
(1 row)
postgres=# SELECT uuid_extract_version(uuidv4());
uuid_extract_version
----------------------
4
(1 row)
postgres=# SELECT uuid_extract_version(uuidv7());
uuid_extract_version
----------------------
7
(1 row)
postgres=# SELECT uuid_extract_timestamp(uuidv7());
uuid_extract_timestamp
----------------------------
2025-05-19 20:50:40.381+00
(1 row)
postgres=# SELECT uuid_extract_timestamp(uuidv7(INTERVAL '1 hour'));
uuid_extract_timestamp
----------------------------
2025-05-19 21:50:59.388+00
(1 row)
postgres=# SELECT uuid_extract_timestamp(uuidv7(INTERVAL '-1 day'));
uuid_extract_timestamp
----------------------------
2025-05-18 20:51:15.774+00
(1 row)
Использование uuidv7()
в качестве первичного ключа таблицы максимально простое, а возможность извлекать временную метку делает UUID
удобным для сортировки и анализа времени создания записи:
CREATE TABLE test (
id uuid DEFAULT uuidv7() PRIMARY KEY,
name text
);
INSERT INTO test (name) VALUES ('foo');
INSERT INTO test (name) VALUES ('bar');
-- this will be sorted to the beginning of the list since we are making it 1h older than the other two
INSERT INTO test (id, name) VALUES (uuidv7(INTERVAL '-1 hour'), 'oldest');
SELECT uuid_extract_timestamp(id), name FROM test ORDER BY id;
uuid_extract_timestamp | name
----------------------------+--------
2025-05-19 19:55:43.87+00 | oldest
2025-05-19 20:55:01.304+00 | foo
2025-05-19 20:55:01.305+00 | bar
(3 rows)
Все эти функции подробно описаны в официальной документации PostgreSQL. А если вас интересуют детали реализации, вы можете изучить соответствующий патч.
Заключение
PostgreSQL 18 приносит практичные улучшения, которые по достоинству оценят опытные разработчики. Нативная поддержка UUIDv7
— незаметное, но весьма значимое нововведение, решающее давние проблемы проектирования баз данных.
UUID
всегда были компромиссом: они надёжны, гарантированно уникальны и эффективно генерируются в распределённых системах — но при этом страдали от проблем с производительностью при использовании в B-tree индексах. UUIDv7
объединяет лучшее из двух миров: глобальную уникальность и упорядоченность, совместимую с B-tree и нагрузками на запись. PostgreSQL 18 делает работу с ними ещё удобнее.

Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм — Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано.
Комментарии (26)
ermadmi78
11.09.2025 18:00Было бы здорово ещё как-то вероятность коллизий оценить.
SergeyProkhorenko
11.09.2025 18:00Вероятность коллизий UUIDv7, сгенерерованных в рамках одного бэкенда, строго равна нулю. Даже если бы в UUIDv7 не было случайной составляющей, а был только таймстемп, работающий в критических режимах как счетчик, коллизии были бы невозможны, как невозможны коллизии при автоинкременте в рамках одного бэкенда.
Вероятность коллизий UUIDv7, сгенерерованных несколькими бэкендами (при одновременной генерации на клиентах, при генерации несколькими микросервисами, при слиянии данных из разных таблиц) тоже можно сделать строго равной нулю, если сдвинуть таймстемпы на разных бэкендах на разные и достаточно большие интервалы. Для этого в функции uuidv7() есть параметр типа "интервал". Но никакой реальной необходимости в этом нет, так как UUIDv7, сгенерерованные несколькими бэкендами, не столкнутся даже без манипуляций с таймстемпами.
В остальных случаях вероятность коллизий ненулевая, но пренебрежимо мала. Достоверно оценить ее невозможно. Расчеты игнорируют то существенное обстоятельство, что в таблице с небольшим количеством строк возможность коллизий гораздо меньше, чем в таблице с огромным количеством строк. Формулы обычно исходят из того, что имеется потенциально бесконечно большая таблица, куда непрерывно добавляются строки вплоть до возникновения коллизии идентификаторов, и никакие строки не удаляются. Реальность, конечно, не имеет ничего общего с этой моделью.
Ни одного известного случая истинной коллизии UUID до сих пор не было. Хотя были случаи, когда из-за ошибок в алгоритме сталкивались копии одного и того же UUID.nin-jin
11.09.2025 18:00Тогда формулы такие. Заполненность таблицы тут не имеет значения, ибо коллизия возможна только с тем же моментом времени.
SergeyProkhorenko
11.09.2025 18:00А какова вероятность того, что в таблице окажутся UUIDv7 с одинаковым таймстемпом? Вы это явно упустили в своей формуле. Если вероятность такого события практически нулевая, то уже безразлично, какой длины случайная часть идентификатора - вероятность коллизии будет практически нулевой. Беда всех таких формул, что модель, на которой они основаны, не описывается. При попытке описать модель авторы формул столкнулись бы с тем, что их модель не соответствует реальности
hogstaberg
11.09.2025 18:00Мысль на грани идиотии для исключения коллизии в случае многих бэкэндов: небольшую часть из 62 случайных бит разрешить отдавать под фиксированный id бэкэнда (уникальный для каждого из них). Тогда даже если каким-то чудом совпадут временная и случайная часть битов, гарантированно уникальная фиксированная часть обеспечит отсутствие коллизии. Да, теряем на размере случайной части, но, если не увлекаться и условные 6-8 бит позволить откусывать, то выглядит всё ещё не так страшно. Впрочем это, очевидно, будет уже не совсем UUIDv7)
SergeyProkhorenko
11.09.2025 18:00Так было в UUIDv1 (см. сегмент node). Однако, если это требует централизованной координации, то это неудобно и не всегда возможно. Если же используется MAC-адрес, то это нарушает конфиденциальность. И, вдобавок, были реальные случаи совпадения MAC-адресов.
hogstaberg
11.09.2025 18:00Так было в UUIDv1
Угу. Только там оно было, ЕМНИП, 48-битным куском ВМЕСТО рандомных бит, что уже действительно сильно на случайность и стойкость к подбору влияет. А вот оставить ощутимое количество рандомных бит и небольшое поле для предотвращения коллизий - такого там нет.
Ну и централизованная координация не так страшна, как кажется. По-крайней мере её можно автоматизировать и делать на уровне хотя бы того же конфига СУБД однократно при запуске инстанса. А дальше у нас во время работы бесплатно гарантия отсутствия коллизий в распределенной системе без всяких проверок.
И, вдобавок, были реальные случаи совпадения MAC-адресов.
Вот поэтому я за ручное распределение всегда. Если вы делаете подобные штуки для гарантий уникальности, то лучше потратить немного времени и вручную задать значение, чем полагаться на то, что оно автоматом окажется не совпадающим с другими.
Habr4687544
11.09.2025 18:00В идеале еще туда бы идентификатор таблицы добавить, тогда по подвисшему в воздухе id можно будет сказать из какой он таблицы
hogstaberg
11.09.2025 18:00Увы, единственный жизнеспособный вариант будет - использовать oid таблицы. А это уже 4 байта aka 32 бита. В противном случае получится решение, которое не подойдёт тем, у кого таблиц много/не будет иметь удобного-нативного способа напрямую соотнести таблицу с таким полем из UUID. Ну или мы возьмём много бит чтобы хватило всем и сильно на случайной части потеряем, облегчив подбор ключей перебором.
Да и смысл? Если вы записываете/выбираете значение, то вы и так уже знаете с какой таблицей вы работаете.
SergeyProkhorenko
11.09.2025 18:00Этого не сделали исходя из принципа единственной ответственности. UUIDv7 минималистичен. Только ключ, и ничего более.
Есть и более практические соображения. Могут быть две таблицы, например, условно копии в разных схемах, в которых одни и те же UUIDv7 будут первичным ключом. Как понять, к какой из двух таблиц относится "подвисший в воздухе id"?
Тем не менее, есть два способа решения проблемы "подвисшего в воздухе id". Первый - отдельная таблица соответствия UUIDv7 имеющимся таблицам.
Второй способ - использование длинного идентификатора (например 160 бит), в старших разрядах которого будет UUIDv7, а в младших - ключ на таблицу метаданных (имя таблицы "подвисшего в воздухе id" и др.) и опционально контрольная сумма. Длинные идентификаторы, содержащие UUID, прямо предусмотрены стандартом RFC 9562. Но во втором способе придется хранить такие идентификаторы как строки, а не как бинарный тип UUID, что замедлит работу БД. К сожалению, в PostgreSQL нет типа данных "длинный UUID", а далекие от системного анализа разработчики считают, что и 128 бит - слишком много.
Akina
11.09.2025 18:00Вероятность коллизий UUIDv7, сгенерерованных несколькими бэкендами (при одновременной генерации на клиентах, при генерации несколькими микросервисами, при слиянии данных из разных таблиц) тоже можно сделать строго равной нулю, если сдвинуть таймстемпы на разных бэкендах на разные и достаточно большие интервалы.
Это как это КОНСТАНТА может обнулить вероятность коллизии? Коллизия без сдвига возникает при абсолютно одновременной генерации (чисто по компоненте штампа времени, конечно, без учёта рандомной составляющей), а при сдвиге - когда разность времени генерации абсолютно равна этому сдвигу.
SergeyProkhorenko
11.09.2025 18:00Если на одном бэкенде UUIDv7 генерятся с таймстемпом 21-го века, на втором - 22-го века и т.д., то UUIDv7 с разных бэкендов не столкнутся в течение 100 лет
nin-jin
11.09.2025 18:00Стоит иметь ввиду, что при монотонных айдишниках ребалансировки b-tree происходят существенно чаще, чем при рандомных. А это не дешёвая операция.
SergeyProkhorenko
11.09.2025 18:00Всё же при выборе типа идентификатора или ключа имеет смысл опираться не на противоречивые теоретические аргументы за и против, не имеющие численного выражения, а на бенчмарки. А бенчмарки говорят, что UUIDv7 и автоинкремент обеспечивают примерно одинаковый темп вставки и поиска записей, а UUIDv4 существенно им уступает.
См. статью UUID Benchmark War
Правда и бенчмарки тоже дают не полную картину. UUIDv7 по сравнению с автоинкрементом позволяют избавиться от лишних расчетов и таблиц при слиянии данных. Ведь при использовании автоинкремента необходима замена ключей. Но разницу так просто не посчитать
mayorovp
11.09.2025 18:00Эффект от кеширования страниц "срабатывает" при каждой записи, а балансировка случается только при заполнении узла.
c0ntr0ller
11.09.2025 18:00Получается можно еще и на столбце временнОй метки сэкономить?
SergeyProkhorenko
11.09.2025 18:00Можно, но стандарт RFC 9562 не рекомендует, исходя из принципа единственной ответственности. Например, при использовании периодически изменяющегося параметра сдвига таймстемпа будут генериться прекрасные уникальные идентификаторы, используемые в качестве первичных ключей, но их уже нельзя будет использовать для извлечения реального таймстемпа
SergeyProkhorenko
11.09.2025 18:00Поправка: RFC 9562 разрешает парсинг UUIDv7 при крайней необходимости.
Akina
Описание несколько неточное, и даже вследствие этой неточности имхо противоречащее ранее написанному:
В реализации постгресса случайная часть сокращена до 62 бит, а 12 бит хоть и описываются как "sub-millisecond", на самом деле если и не являются, то вполне могут считаться самым что ни на есть вульгарным автоинкрементом (правда, не смотрел, действительно ли там плюсуют по единичку, или используется рандомное приращение в малом диапазоне, но сути это не меняет). Что уж никак не есть рандом. К слову, это описывается стандартом.
К тому же 4-битное поле версии располагается между штампом времени и этим 12-битным дополнением, но в рамках одного инстанса и одного пакета изменение версии нереально, так что его можно считать константой. И, рассматривая сортируемость, игнорировать.
SergeyProkhorenko
Не надо домыслов. Субмиллисекунды совершенно честные. Для этого разработчик (Андрей Бородин) залез в самые глубинные потроха операционной системы и извлек то, о чем 99.9999% программистов не знают. Кстати, для маков не удалось достать 12 битов времени - там только 10, а 2 младших бита - рандомные. Честные субмиллисекунды, в отличие от счетчика, дают почти безупречную (но не гарантированную) монотонность идентификаторов, даже если идентификаторы генерятся на разных бэкендах.
Кстати, в переведенной статье ошибочно утверждается (вообще статья крайне неудачная), что "12-битная субмиллисекундная составляющая ... гарантирует монотонность всех
UUIDv7
, сгенерированных в рамках одного backend-процесса Postgres (одной сессии)". На самом деле все UUIDv7, сгенерерованные в рамках одного бэкенда будут гарантированно монотонными независимо от наличия или отсутствия субмиллисекундной составляющей. Монотонность гарантируется таймстемпом, а если при лавинообразной генерации точность таймстемпа недостаточна, то весь таймстемп начинает работать как счетчик - это особенность алгоритма в PostgreSQL 18.Akina
Минутку... не хотите ли вы сказать, что алгоритм получения timestamp гарантирует ко всему прочему ещё и уникальность? Причём вы же говорите именно о встроенном типе данных ("точность таймстемпа недостаточна" - это же о нём, а не о нём плюс 12 дополнительных битов), который вроде как имеет точность в микросекунду...
SergeyProkhorenko
Да, на одном бэкенде алгоритм получения timestamp (включая его дополнительную 12-битную субмиллисекундную часть), который в критических режимах работает как счетчик, гарантирует уникальность (строго нулевая вероятность коллизий). В случае нескольких бэкендов вероятность коллизий не нулевая, но пренебрежимо мала.
Akina
Давайте пока оставим субмиллисекундную часть, которая существует только где-то в недрах сервера и пользователю недоступна (ведь недоступна же, правда?). точность формата штампа времени - микросекунда. И по вашим словам я понимаю так, что из-за особенностей реализации при массированной вставке записей в таблицу с полем автогенерации текущего штампа времени с микросекундной точностью (не UUID) вставляемые значения будут уникальны вне зависимости от каких-либо обстоятельств...
SergeyProkhorenko
12-битная субмиллисекундная часть, конечно же, доступна пользователю, как и любой другой сегмент UUIDv7. Она обеспечивает точность приблизительно 250 наносекунд. Но если UUIDv7 не был сгенерирован в PostgreSQL, а пришел с клиента, то вместо субмиллисекундной части будет случайное значение. Поэтому функция uuid_extract_timestamp ( uuid ) работает только с первыми 48 битами таймстемпа, которые дают точность 1 миллисекунда (а не микросекунда, как Вы написали). Если нужно извлечь таймстемп с точностью 250 наносекунд, то придется делать собственную функцию. При этом не получится воспользоваться стандартным типом timestamptz, поскольку его точность всего 1 микросекунда.
Для справки: миллисекунда > микросекунда > наносекунда
И да, на одном бэкенде уникальность гарантирована.
x4m
Тут довольно прикольная вероятностная идея, что время разделяет данные на +\- одинаковые бакеты в четверть микросекунды. А случайные числа - их там около 60 бит - должны разделить на уникальные все UUID в мире, которые сгенерированы в эту четверть микросекунды.