Что такое UUIDv7?

Оглавление

  1. Введение
  2. Структура UUIDv7
  3. Сравнение UUIDv7 с UUIDv1, UUIDv4 и UUIDv6
  4. Проблемы UUIDv1, UUIDv4 и UUIDv6
  5. Как UUIDv7 решает эти проблемы?
  6. Примеры генерации UUIDv7 в разных языках
  7. Заключение

Введение

UUID давно стал одним из ключевых инструментов в распределенных системах:

  • простая генерация без координации,
  • глобальная уникальность,
  • переносимость между языками и БД.

Но у классических версий есть системные недостатки. UUIDv4 отлично скрывает контекст, но ведет себя как равномерный шум – плохая локальность в B-деревьях, фрагментация индексов, дорогие диапазонные запросы по “последним” данным. UUIDv1 частично решает упорядоченность, но несет риски приватности (MAC/узел), использует неудобную эпоху 1582 с 100-нс тиком и не сортируется побайтно по времени. UUIDv6 переупорядочивает v1 для лексикографической сортировки, но сохраняет его временную модель и операционную сложность (счетчики/регресс часов).

UUIDv7 – ответ на эти боли. Он комбинирует Unix-время в миллисекундах в старших 48 битах (естественная лексикографическая сортировка по времени) с 74 битами энтропии (распределенная генерация без координации и практическая невозможность коллизий). То есть вы получаете почти “последовательные” ключи для СУБД, не отказываясь от свойств UUID. Дополнительно, v7 не раскрывает MAC/узел и допускает монотонный режим внутри одной миллисекунды (счетчик) – полезно для высоких QPS.

Когда это важно на практике

  • OLTP-БД и индексы: вставки ближе к “хвосту” индекса, меньше page split, компактнее B-дерево, быстрее “последние N” и range-сканы по времени.
  • Событийные журналы, очереди, CDC: естественный порядковый ключ, который еще и несет момент времени.
  • Многоязычные стеки/микросервисы: единый формат, поддерживаемый экосистемой (Go/Rust/Java/Python/Node), без централизованных генераторов как у Snowflake.
  • Безопасность и приватность: отсутствие узловых идентификаторов (в отличие от v1/v6).

Структура UUIDv7

Рисунок 1: Формат и поля UUID версии 7 (128 бит). Первые 48 бит содержат метку времени Unix (миллисекунды с 1 января 1970 UTC), далее 4 бита – код версии “7”, 2 бита – вариант (стандарт IETF), и оставшиеся 74 бита заполняются случайными данными.

UUID версии 7 представляет собой 128-битный идентификатор, в котором поля расположены следующим образом:

  • Unix Timestamp (48 бит): Самые значимые 48 бит содержат время Unix в миллисекундах (число миллисекунд с полуночи 1 января 1970 г. UTC, без учета високосных секунд). Это поле обеспечивает хронологическую сортировку идентификаторов по времени их создания.
  • Version (версия, 4 бита): Код версии UUID, равный 0111_2 (то есть 7 в десятичном выражении) для UUIDv7. Эти биты занимают позиции 48–51 и однозначно указывают, что данная последовательность бит соответствует именно UUID версии 7.
  • Rand_a (случайные биты A, 12 бит): Далее следуют 12 бит псевдослучайных данных. Они могут использоваться просто как случайность для обеспечения уникальности, либо часть из них (до 12 бит) опционально может хранить дробную часть метки времени (например, дополнительные разряды для микросекунд) с целью повышения монотонности, если необходимо генерировать несколько UUID в пределах одной миллисекунды. Если дополнительные биты времени не используются, эти 12 бит полностью случайны.
  • Variant (вариант, 2 бита): Поле варианта (variant) указывает схему интерпретации UUID. Для всех UUID, соответствующих стандарту RFC 4122/IETF, вариант равен 10_2. В UUIDv7 эти 2 бита занимают позиции 64–65 и всегда установлены в 10.
  • Rand_b (случайные биты B, 62 бит): Оставшиеся 62 бита (позиции 66–127) содержат случайное значение, распределенное равномерно. Эти биты обеспечивают высокую энтропию и уникальность UUIDv7. В некоторых реализациях сюда же может быть включен небольшой счетчик для гарантированной уникальности/монотонности при генерации множества идентификаторов в одну миллисекунду, однако общий случай предполагает заполнение этого пространства криптографически случайными данными.

В итоге UUIDv7 сохраняет классический формат представления UUID из 36 символов (например: 018AB868-D2DD-78F1-BC79-FCC52619C0E3), где каждая часть (8-4-4-4-12 символов в шестнадцатеричном виде) соответствует вышеописанным полям. Благодаря включению метки времени Unix в старшие биты, такие UUID по своему естественному порядку (например, при лексикографическом сравнении или сортировке как беззнаковые 128-битные числа) упорядочены по времени генерации.

Опциональная монотонность в пределах одной метки времени: Стандарт допускает, что реализация при необходимости может обеспечить строгую монотонность UUIDv7, выпускаемых в одну миллисекунду. Для этого в поле rand_a можно сохранить дополнительную дробную часть времени (например, значение микросекунд или счетчик из нескольких бит), а оставшиеся биты rand_a/rand_b уменьшить на размер этого счетчика. В таком случае при генерации нескольких UUID в пределах одного миллисекундного тика они будут отличаться не только случайной частью, но и увеличивающимся значением счетчика, что гарантирует сохранение сортировки по порядку выпуска. Однако даже без этой функции, за счет случайных бит, вероятность коллизий при совпадении метки времени пренебрежимо мала, а сортировка по старшим 48 битам остается обеспеченной.

Сравнение UUIDv7 с UUIDv1, UUIDv4 и UUIDv6

Рассмотрим, чем отличается UUID версии 7 от предыдущих широко распространенных версий – v1, v4 и v6 – и какие преимущества или недостатки характерны для каждой из них:

  • UUIDv1 (временной, на основе MAC-адреса): В UUID версии 1 128-битное значение включает временную метку с 60-битным отсчетом в 100-нс интервалах (начиная с 15 октября 1582 г.), 14-битный счетчик (clock sequence) и 48-битный идентификатор узла (обычно MAC-адрес). Преимущество v1 – это гарантированная уникальность в пределах узла (благодаря MAC-адресу) и хронологическая составляющая. Однако есть серьезные недостатки: во-первых, MAC-адрес в открытом виде вписывается в UUID, что несет риск приватности и безопасности (может раскрывать информацию о хосте). Во-вторых, время отсчитывается от нестандартной эпохи 1582 года с высокой частотой, что затрудняет работу с этой меткой вне специального кода – нестандартный формат времени (Gregorian Epoch 100 ns) плохо поддерживается в обычных библиотеках и числовых типах. Наконец, байтовое расположение полей v1 таково, что простое побайтовое сравнение UUIDv1 не отражает хронологический порядок – время разбито на несколько частей (low, mid, high) в разных местах, поэтому для сортировки по времени требуется парсинг UUID и перестановка битов. Таким образом, как ключ в базе данных UUIDv1 менее удобен: без специальной обработки они не будут естественно отсортированы по времени.
  • UUIDv4 (случайный): UUID версии 4 полностью состоит из случайных бит (кроме фиксированных полей версии и варианта). Он крайне прост в генерации и не раскрывает никаких деталей о времени или системе – очень хорошо с точки зрения приватности. Вероятность коллизии для криптографически стойкого генератора пренебрежимо мала (~2^122 возможных значений). Однако отсутствие какой-либо упорядоченности – существенный недостаток при использовании v4 в качестве ключа: последовательные UUIDv4 статистически никак не коррелируют и будут равномерно распределены по пространству, из-за чего при вставке записей в базу данные попадают в случайные места индекса. Такая плохая локальность ведет к фрагментации B-деревьев, росту размеров индексов и снижению скорости вставки и выборки (особенно по диапазонам недавних данных). Другой нюанс – генерация криптографически случайных 122 бит может оказаться относительно дорогой операцией при очень высоких нагрузках на генератор (хотя на практике современные генераторы справляются с миллионами UUIDv4 в секунду).
  • UUIDv6 (упорядоченный по времени, совместимый с v1): Версия 6 была предложена как переупорядоченная схема UUIDv1 для улучшения сортировки и индексной локальности. По структуре UUIDv6 сохраняет 60-битовую метку времени v1, но размещает ее биты в старших разрядах UUID – сначала идут 48 бит времени (старшие из 60), затем 4 бита версии (значение 6), затем оставшиеся 12 бит времени. После них следуют стандартные 2 бита варианта, 14 бит счетчика и 48 бит узла (как в v1). Таким образом, UUIDv6 решает проблему сортировки – если генераторы v6 на разных узлах синхронизированы по времени, идентификаторы будут лексикографически упорядочены по времени без парсинга. Кроме того, для v6 допускается (и рекомендуется) заполнение 48 бит “узла” случайным значением вместо реального MAC-адреса, чтобы устранить утечку приватных данных. В результате UUIDv6 сочетает хронологическую упорядоченность с высокой энтропией (при случайном узле). Но недостатки все же есть: v6 по-прежнему опирается на устаревшую модель времени v1 и механизм счетчика. То есть для реализации v6 требуется отслеживать состояние (последний timestamp и clock sequence) аналогично v1. Кроме того, на момент стандартизации v6 не получил широкого распространения – фактически вместо него предпочтение отдается UUIDv7, если нет необходимости в строгой совместимости с существующими UUIDv1.
  • UUIDv7 (временной, на основе Unix-времени): Новый стандартный формат, сочетающий достоинства перечисленных версий. В отличие от v1/v6, версия 7 использует привычную Unix-эпоху в миллисекундах для метки времени, что упрощает взаимодействие и хранение времени. При этом, в отличие от v1, в UUIDv7 отсутствуют привязанные к оборудованию данные: никакого MAC-адреса или иных постоянных узловых идентификаторов – только время и случайность. 74 случайных бита дают колоссальное пространство для уникальных значений на каждый миллисекундный интервал, поэтому вероятность коллизий ничтожно мала, даже если множество узлов генерирует UUIDv7 параллельно. Как и v6, версия 7 естественно сортируется по времени (в старших битах), благодаря чему близкие по времени UUID сгруппированы в индексах баз данных. В сравнении с v4, UUIDv7 обеспечивает лучшее индексное поведение при вставках (почти как у последовательных ключей) ценой минимального раскрытия информации (в явном виде – только время создания). В отличие от v1/v6, генерация UUIDv7 не требует отслеживания состояния (достаточно текущего времени и источника случайности), что упрощает реализацию и улучшает масштабируемость генераторов.

Таким образом, UUIDv7 можно рассматривать как компромисс между полностью случайным UUIDv4 и жестко детерминированным (по времени/узлу) UUIDv1: он сохраняет высокую степень случайности для распределенной генерации без конфликтов, но одновременно предоставляет монотонную временную составляющую для упорядоченности. Ниже подробно рассмотрены проблемы предыдущих версий и то, как UUIDv7 их решает.

Проблемы UUIDv1, UUIDv4 и UUIDv6

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

  • Плохая сортировка данных: UUID, не основанные на времени (прежде всего версия 4), при последовательном создании не имеют никакого естественного порядка. В результате новые значения распределяются по индексу случайно, вызывая частые разбиения страниц и рост затрат на I/O. Это проявляется в низкой эффективности индексирования: например, последние вставленные записи оказываются разбросаны по всему B-дереву, ухудшая кеш-локальность. В случае UUIDv1 ситуация лучше с точки зрения хронологии, но из-за разбиения временной метки на части сортировка по bytestring не соответствует порядку времени, требуя парсинга для сравнения по дате. До появления новых форматов это вынуждало разработчиков применять сложные решения – от выделения отдельного последовательного ключа в БД до использования альтернативных алгоритмов генерации (ULID, Snowflake и т.д.).
  • Утечка приватной информации: UUIDv1 содержит 48 бит node ID, обычно представляющие MAC-адрес хоста. Этот адрес может быть извлечен из UUID и потенциально использован злоумышленниками для идентификации устройства, его производителя, или сетевой атаки на обнаруженный интерфейс. Кроме того, метка времени в v1 прямо указывает момент генерации с 100-нс точностью, что тоже может нежелательно раскрывать информацию о системе (например, частоту создания объектов). В виртуальных и контейнерных средах уникальность MAC не гарантируется, что снижает надежность v1. UUIDv4, напротив, полностью прозрачен с точки зрения содержимого (не несет никаких сведений о системе или времени), что зачастую предпочтительнее. UUIDv6 допускает отказ от реального MAC, однако по-прежнему явно включает временную метку.
  • Отсутствие глобальной монотонности: Для распределенных систем важна возможность получать уникальные идентификаторы, которые не только уникальны, но и по возможности отражают порядок событий. Ни UUIDv1, ни v4 гарантировать строгую монотонно увеличивающуюся последовательность не могут – v1 может порождать одинаковые метки времени при генерации нескольких UUID “в одном тике” и полагается на счетчик для различения, а v4 генерируется случайно и не имеет понятия “порядок” вовсе. Если системные часы идут неравномерно или назад, v1/v6 требуют менять clock sequence, что нарушает упорядоченность. В итоге в наборе UUIDv1/v4 могут встречаться “скачки” порядка. Это осложняет анализ по временной оси (нельзя просто отсортировать набор UUIDv4 и получить хронологию).
  • Неудобный формат временной метки: Как отмечалось, UUIDv1/v6 используют нестандартный отсчет времени (1582 г., 100 нс интервалы). Это затрудняет хранение и использование таких меток вне контекста UUID. Например, преобразование timestamp из UUIDv1 в обычное время требует специальной процедуры. Кроме того, размерность 100 нс плохо представима 32- или 64-битными числами с плавающей точкой без потери точности. Это скорее эргономический недостаток, но для некоторых языков и СУБД он имел значение (отсутствие встроенных типов для 128-битного времени).
  • Производительность генерации: В некоторых сценариях узким местом может стать скорость выпуска большого количества UUID. Для UUIDv1 генерация включает чтение системных часов высокой точности и потенциально обращение к MAC-адресу (или другому уникальному идентификатору узла), а также управление состоянием (счетчиком) при обнаружении дублирующегося времени или регресса часов. Это требует аккуратной реализации, особенно в многопоточных приложениях. Для UUIDv4 основная нагрузка – на генератор случайных чисел; криптографически стойкий ГСЧ может ограничивать скорость получения десятков миллионов UUID в секунду, либо требовать буферизации. Наконец, как упомянуто, случайный характер v4 негативно влияет на производительность операций в БД (размеры индексов, число кэш-промахов и т.п.), хотя сама по себе генерация v4 довольно проста.

Подводя итог, традиционные версии UUID не удовлетворяют полностью потребностям современных распределенных систем: v4 – отлично по уникальности и безопасности, но плохо для баз данных; v1/v6 – все хорошо с упорядоченностью, но имеют проблемы приватности и неудобства реализации/использования. UUIDv7 был предложен специально для устранения этих недостатков.

Как UUIDv7 решает эти проблемы?

Формат версии 7 разработан с учетом описанных недостатков, и его технические особенности устраняют или смягчают многие проблемы предшественников:

  • Естественная сортировка и локальность в индексах: UUIDv7 обеспечивает временную упорядоченность за счет включения метки времени в самые значимые биты. Это означает, что при сравнении двух UUIDv7 “как есть” больший (в беззнаковом смысле) идентификатор гарантированно был сгенерирован не раньше меньшего. Новые UUIDv7, создаваемые подряд, будут практически монотонно увеличиваться (если системные часы идут вперед), и вставляться в конец индексов, как и автоинкрементные ключи. Такая последовательность существенно улучшает локальность ссылок в B-деревьях и снижает фрагментацию по сравнению с v4: эксперименты показывают, что замена UUIDv4 на UUIDv7 может ускорить вставки и уменьшить размер индексов ощутимо (десятки процентов выигрыша). В отличие от v1, для сортировки не требуется парсинг – стандартное лексикографическое сравнение строк UUIDv7 или чисел 128-битного типа соответствует порядку времени. Это упрощает как построение запросов (например, можно эффективно использовать диапазонные условия WHERE uuid >= X для выборки по дате), так и работу систем репликации/тиражирования, где упорядоченные идентификаторы облегчают выявление последовательности операций.
  • Монотонность при высоких скоростях генерации: Даже если в одной системе генерируется множество UUIDv7 в пределах одной миллисекунды, алгоритм может сохранить их в отсортированном порядке. Разработчики стандарта предусмотрели возможность использования части случайных битов как счетчика или суб-миллисекундного таймстемпа. Практически это означает, что генератор может детектировать повторение миллисекундного значения времени и, скажем, увеличивать 12-битный счетчик (вместо полностью случайного rand_a). Таким образом, второй UUID в той же миллисекунде будет иметь значение rand_a чуть больше первого, обеспечивая сохранение общего порядка. Даже без этого механизма вероятность того, что два UUIDv7, выпущенные в одну миллисекунду, окажутся “неупорядоченными”, крайне мала. Если генератор полностью случайно выбирает 74 бита, то они распределены равномерно – вероятность того, что второй сгенерированный UUIDv7 окажется лексикографически меньше первого, составляет ~50%, но на практике это легко устраняется сортировкой по времени (поскольку время одинаковое, можно вторичным ключом считать сам UUID). Многие библиотеки реализации v7 уже включают опцию монотонного режима (аналогично ULID). Важно отметить, что глобальная монотонность (между узлами) при несинхронизированных часах не гарантируется – если часы одного сервера отстают, его UUIDv7 могут “вставать” в ряду раньше, чем идентификаторы с другого сервера, сгенерированные ранее по реальному времени. Однако в пределах одного узла с ненарушенной хронологией генерации последовательность будет монотонной. А благодаря большим случайным полям UUIDv7 устойчив к конфликтам даже при небольших расхождениях часов между узлами.
  • Устранение утечки приватных данных: В UUIDv7 полностью отсутствуют поля, прямо привязанные к оборудованию или уникальные для узла (в отличие от v1/v6). Вместо MAC-адреса используется случайная компонента, что исключает возможность узнать что-либо о происхождении идентификатора, кроме времени создания. Сама по себе метка времени (48 бит мс) в большинстве случаев не считается секретной информацией – она лишь указывает приблизительный момент генерации ID. Тем не менее, следует понимать, что UUIDv7 не анонимен во времени: по нему можно определить, когда был создан объект (с точностью до миллисекунды). В некоторых случаях это может рассматриваться как нежелательное раскрытие (например, по серии UUID можно оценить нагрузку на систему в те или иные периоды). Если требуется полностью скрыть время создания, по-прежнему лучше использовать UUIDv4 или UUIDv8 (кастомный). Но в целом компромисс UUIDv7 считается удачным: он не раскрывает информации о хосте или о количестве сгенерированных объектов (вне текущей миллисекунды), а временная компонента полезна для системной сортировки.
  • Простота и эффективность генерации: В реализации UUIDv7 отпадает необходимость хранить глобальное состояние (как clock sequence в v1/v6). Генератор может быть чистой функцией: взять текущее время (можно из стандартных часовых API миллисекундной точности) и сгенерировать 74 случайных бита. Благодаря этому генерация масштабируется в многопоточных и распределенных системах без блокировок и координации. Современные криптографические ГСЧ способны выдавать десятки миллионов случайных бит в секунду, что позволяет реализовать очень высокие темпы генерации UUIDv7 (порядка 10 млн UUID/сек на узел и более). При необходимости можно использовать аппаратные генераторы или буферизацию случайных данных. В плане нагрузки на систему генерация UUIDv7 сопоставима с v4 (требуется получить 74 случайных бита вместо 122, что даже чуть легче) – разница в производительности минимальна. При этом, как отмечалось, операции с такими UUID в базе данных работают эффективнее: улучшение локальности записей уменьшает размер индексов и число операций ввода-вывода. Например, эксперименты с PostgreSQL 15 показали, что таблица на 10 млн записей с UUIDv7 занимает значительно меньше места и дает более быстрые точечные и диапазонные запросы по сравнению с той же таблицей на UUIDv4. Таким образом, UUIDv7 объединяет преимущества последовательных ключей и глобально уникальных случайных идентификаторов, позволяя упростить архитектуру (отказаться от составных ключей и последовательностей) без ущерба для производительности хранения данных.
  • Низкая вероятность коллизий и распределенная генерация без координации: UUIDv7 сохраняет главный принцип UUID – независимую генерацию без централизованного контроля – и достигает этого за счет большой случайной составляющей. Каждый UUIDv7 содержит 74 бит энтропии, которые генерируются случайно на каждом узле. Пространство 2^74 (~1.8 * 10^22) возможно создавать заново в каждую миллисекунду, так что риск создания двух одинаковых UUIDv7 практически нулевой. Вероятность случайной коллизии между двумя UUIDv7 сопоставима с таковой у UUIDv4 (несколько меньше, но тоже пренебрежимо мала – для сравнения, 2^74 возможных значений достаточны, чтобы при генерации миллиардов идентификаторов в секунду на протяжении тысяч лет вероятность совпадения оставалась бы крайне низкой). Таким образом, распределенная генерация UUIDv7 на множестве узлов не требует координации: достаточно, чтобы каждый узел имел достаточно качественный генератор случайностей и относительно правильные часы. Согласно спецификации, при высоких требованиях к предотвращению коллизий допускается внедрение дополнительного уникального узлового идентификатора (как часть случайных бит или как префикс в кастомной версии 8), однако в общем случае использование криптостойкого ГСЧ само по себе обеспечивает необходимую надежность. Практически это означает, что, внедрив UUIDv7, вы можете генерировать уникальные ключи на любой числе серверов (даже не синхронизированных друг с другом) – совпадения маловероятны, а новые значения автоматически приблизительно упорядочены по времени поступления с каждого узла.

Примеры генерации UUIDv7 в разных языках

UUIDv7 уже поддерживается во многих языках программирования через сторонние библиотеки (а местами и встроенными средствами). Рассмотрим, как можно генерировать UUID версии 7 на популярных платформах.

Go (Golang)

В языке Go доступно несколько реализаций UUIDv7. Одна из самых популярных – пакет github.com/gofrs/uuid (fork официальной библиотеки Go UUID). Начиная с версии v2.0.0 он поддерживает все стандартизованные версии UUID, включая v6 и v7. Пример использования:

import "github.com/gofrs/uuid"

id, err := uuid.NewV7()        // сгенерировать новый UUIDv7
if err != nil {
    panic(err)
}
fmt.Println(id.String())       // вывод результата в виде строки

В данном случае вызов uuid.NewV7() возвращает объект UUID версии 7, собранный из текущего системного времени и случайных чисел. Полученный идентификатор можно форматировать привычным образом (методом .String() или через fmt), он будет выглядеть как строка из 36 символов (группы 8-4-4-4-12 в hex). Библиотека gofrs/uuid реализует UUIDv7 в соответствии с официальной спецификацией RFC 9562. Аналогичную функциональность предоставляет пакет google/uuid (поддержка v7 появилась около 2023 года в соответствии с новым стандартом).

Можно также сгенерировать UUIDv7 “вручную”, без сторонних пакетов – для учебных целей. Ниже приведен упрощенный пример, как это делается, используя только стандартную библиотеку Go:

import (
    "crypto/rand"
    "encoding/hex"
    "time"
)

func NewUUIDv7() string {
    // 1. Получаем 16 случайных байт
    b := make([]byte, 16)
    _, err := rand.Read(b)    // криптографически случайное заполнение
    if err != nil { panic(err) }

    // 2. Записываем 48-битный таймстемп (мс с эпохи) в первые 6 байт (big-endian)
    unixMs := uint64(time.Now().UnixMilli())
    b[0] = byte(unixMs >> 40)
    b[1] = byte(unixMs >> 32)
    b[2] = byte(unixMs >> 24)
    b[3] = byte(unixMs >> 16)
    b[4] = byte(unixMs >> 8)
    b[5] = byte(unixMs)

    // 3. Устанавливаем версию и вариант
    b[6] = (b[6] & 0x0F) | 0x70  // версия: 0b0111xxxx
    b[8] = (b[8] & 0x3F) | 0x80  // вариант: 0b10xxxxxx

    // 4. Форматируем как строку UUID (8-4-4-4-12)
    uuid := hex.EncodeToString(b)
    return uuid[0:8] + "-" + uuid[8:12] + "-" + uuid[12:16] + "-" +
           uuid[16:20] + "-" + uuid[20:]
}

Эта функция создает UUIDv7, следуя алгоритму: генерирует случайные байты, вставляет в них метку времени Unix (мс), проставляет нужные биты версии/варианта и формирует строковое представление. Выход будет, например: 018ab868-d2dd-78f1-bc79-fcc52619c0e3. Рекомендуется все же использовать проверенную библиотеку (чтобы не допустить неточностей в битовых операциях), но суть генерации показана.

Rust

В экосистеме Rust поддержка UUIDv7 появилась в популярном крейте uuid (версия 1.0+). Этот крейт реализует все стандартные версии UUID (1, 3, 4, 5, 6, 7, 8) – для генерации v7 необходимо включить соответствующую Cargo-фичу “v7”. Пример (с использованием последней версии 1.3+):

use uuid::Uuid;
fn main() {
    let uuid7 = Uuid::now_v7();   // генерирует UUIDv7 с текущим временем
    println!("{}", uuid7);
}

Здесь Uuid::now_v7() возвращает новый UUID версии 7, автоматически получая системное время. (Альтернативно можно использовать Uuid::new_v7(ts, rng), передав явный timestamp и генератор случайных чисел, если требуется нестандартное время или контроль энтропии.) При выводе мы получим строку формата UUID. Крейту uuid доверяют крупные проекты; его реализация основана на финальной спецификации IETF RFC 9562 и рекомендуется к использованию в любых приложениях.

Стоит отметить, что на конец 2025 года в стандартной библиотеке Rust UUIDv7 отсутствует, но внешний крейт полностью закрывает эту потребность. Кроме того, существуют альтернативные крейты, например uuid7, но преимущество основного uuid – комплексная поддержка всех версий и удобные инструменты (парсинг, форматирование, Serde и т.д.).

Java

В Java (и JVM вообще) UUIDv7 поддерживается сторонними библиотеками. Стандартный класс java.util.UUID пока реализует только v3/v4/v5. Однако существуют популярные библиотеки, например UUID Creator (от автора f4b6a3) и Java UUID Generator (JUG), которые уже сейчас умеют генерировать v6 и v7.

UUID Creator – легкая библиотека с удобным API для разных версий UUID. С ее помощью можно создать UUIDv7 следующим образом:

import com.github.f4b6a3.uuid.UuidCreator;

UUID id = UuidCreator.getTimeOrderedEpoch(); // сгенерировать UUIDv7
System.out.println(id);

Метод getTimeOrderedEpoch() возвращает UUID версии 7 (в документации его называют “упорядоченный по времени с Unix-эпохой”). Библиотека сама берет текущий timestamp в мс и случайные числа. На выходе получается экземпляр стандартного класса java.util.UUID, совместимый со всеми Java API. Библиотека UUID Creator обеспечивает высокое качество случайности (с использованием SHA-256 DRBG по умолчанию) и соответствует черновику IETF UUID revision.

JUG (Java UUID Generator) – более старая библиотека от автора FasterXML (Jackson), также недавно обновленная для поддержки версий 6 и 7. Ее использование немного отличается: JUG предоставляет генераторы (implementations of UUIDGenerator). Для UUIDv7 предназначен так называемый time-based epoch генератор. Пример:

import com.fasterxml.uuid.Generators;
import com.fasterxml.uuid.impl.TimeBasedEpochGenerator;

TimeBasedEpochGenerator gen = Generators.timeBasedEpochGenerator();
UUID uuid7 = gen.generate();
System.out.println(uuid7);

Здесь Generators.timeBasedEpochGenerator() возвращает объект, генерирующий UUID по времени Unix (версия 7). Внутри он следит за монотонностью: JUG может использовать счетчик, если обнаружит несколько вызовов в одну миллисекунду. Полученный UUID – опять же стандартный Java UUID. Библиотека JUG поддерживает и v1 (Gregorian time), и v4, так что для явного выбора v7 нужно использовать именно “Epoch”-метод. Согласно описанию проекта, JUG начиная с версии 4.0 генерирует UUIDv1–v7 в соответствии с IETF RFC 4122bis (проект обновления стандарта).

Обе библиотеки (UUID Creator и JUG) распространяются через Maven Central, их легко подключить в проекты на Spring Boot, Jakarta EE и т.д. При выборе стоит учитывать, что JUG – более “низкоуровневый” и гибкий (можно задавать собственный источник рандома, свой инкремент и т.п.), тогда как UUID Creator предлагает более простой высокоуровневый API.

Заключение

UUIDv7 – это практичный “дефолт» для современных backend-систем: естественная сортировка по времени (48-бит Unix-мс в старших разрядах) + высокая энтропия (74 случайных бита) без утечек узловых идентификаторов. Он закрывает боли v4 (плохая локальность индексов) и v1/v6 (приватность, нестандартная эпоха), упрощает анализ по времени и ускоряет типичные операции БД за счёт лучшей индексной структуры.

Когда НЕ выбирать v7

  • Строгое требование не раскрывать время создания -> используйте v4 или кастомный v8.
  • Нужна глобально тотальная монотонность при дрейфе часов между датацентрами потребуется внешняя синхронизация/координация (NTP/PTP, ламинатор, логический/векторный порядок).

Рекомендации по внедрению

  1. Библиотека: берите зрелую реализацию (Go gofrs/uuid, Rust uuid с now_v7, Java UUID Creator/JUG).
  2. Часы: следите за монотонностью системного времени; включайте «монотонный» режим генератора при высоком QPS.
  3. Индексы: в PostgreSQL – btree по UUIDv7 обычно достаточно; при частых «последних N» добавьте композитный индекс (created_at, id) или храните created_at для явных запросов по времени.
  4. Миграция:
    • для новых таблиц – сразу uuidv7 как PK;
    • для существующих – добавьте колонку id_v7, заполните батчами, переключите внешние ключи, затем декомиссьте старый PK; проверьте триггеры/ORM-маппинги.
  5. Трассировка и аудит: не полагайтесь на id как единственный источник времени – храните отдельный created_at (для TZ/точности/инвариантов).
  6. Тесты и нагрузка: прогоните бенчмарки вставок/сканов v4 vs v7 на ваших данных; измерьте рост/сжатие индексов, page split, latency.

Частые ошибки

  • Ручная сборка бит без масок версии/варианта (0x70 и 0x80) -> получаются невалидные UUID.
  • Доверие системным часам без монотонного режима -> «скачки» порядка при burst-нагрузке в одну миллисекунду.
  • Отсутствие отдельного времени события -> сложные запросы по временным диапазонам и аудит.

Итог

Если вы сегодня используете UUID как PK, v7 – рациональный стандарт по умолчанию: он совместим с экосистемой, улучшает эксплуатационные метрики БД и упрощает жизнь разработчику. Оставляйте v4 там, где важно скрыть время, и дополняйте v7 собственными инвариантами (время, шард-ключи), когда это требует доменная модель.

Loading