Спецификация уникальных идентификаторов UUIDv7 для ключей баз данных и распределенных систем по новому стандарту RFC9562

3f7147899fa84d43b4f98d806f232a78.jpg

Долгожданный стандарт RFC9562 «Universally Unique IDentifiers (UUID)» с тремя новыми версиями идентификаторов UUID (6, 7 и 8) вместо малопригодного RFC4122 наконец-то вступил в силу. Я участвовал в разработке нового стандарта. Обзор стандарта можно посмотреть в статье.

Введенные новым стандартом идентификаторы седьмой версии UUIDv7 — это лучшее, что теперь есть для ключей баз данных и распределенных систем. Они обеспечивают такую же производительность, как и bigint. UUIDv7 уже реализованы в том или ином виде в основных языках программирования и в некоторых СУБД.

Сгенерированные UUIDv7 имеют все преимущества UUID и при этом упорядочены по дате и времени создания. Это ускоряет поиск индексов и записей в БД по ключу в формате UUID, значительно упрощает и ускоряет базы данных и распределенные системы. Неупорядоченность значений UUID прежде сдерживала использование UUID в качестве ключей и вынуждала разработчиков выдумывать собственные форматы идентификаторов или довольствоваться последовательными целыми числами в качестве ключей.

Черновик стандарта активно обсуждался на Хабре в апреле 2022 года в комментариях к статье «Встречайте UUID нового поколения для ключей высоконагруженных систем».

Разные участники разработки нового стандарта придерживались различных взглядов, и практически все обсуждавшиеся альтернативные варианты структуры UUIDv7 вошли в стандарт. Поэтому теперь перед разработчиками возникает вопрос, какую из множества возможных спецификаций UUIDv7 реализовывать и применять. Также для массового перехода на UUIDv7 нужна дополнительная функциональность, повышающая привлекательность UUIDv7 для разработчиков и бизнеса.

Предложенная мной ниже спецификация UUIDv7 с дополнительной функциональностью описывает максимально надежный и удобный вариант структуры UUIDv7 для самых сложных и высоконагруженных информационных систем. Функциональность упорядочена по приоритету реализации

Предложенная структура UUIDv7

Длина сегмента, битов

Поле в RFC9562

Содержимое сегмента

48

unix_ts_ms

Таймстемп (метка времени)

4

ver

Версия

1

rand_a

Сегмент счетчика, инициализируемый нулем

11

rand_a

Сегмент счетчика, инициализируемый псевдослучайным числом

2

var

Вариант

6

rand_b

Сегмент счетчика, инициализируемый псевдослучайным числом

56

rand_b

Сегмент UUIDv7, заполняемый псевдослучайным числом

64

Опциональный сегмент справа от UUID в ключевых столбцах БД

1. Сдвиги значения метки времени для максимальной производительности (разрешено RFC9562)

Описание

Сдвиги значения метки времени:

  • Приблизительно миллисекундная точность метки времени, без опционального субмиллисекундного сегмента, с отсечением правых 10 битов вместо деления микросекунд на 1000. Это ускорит генерацию UUID по сравнению с математически корректным делением

  • Случайные сдвиги метки времени на фиксированные интервалы при генерации UUID для того, чтобы распределить индексы в БД между несколькими страницами и тем самым уменьшить очереди на чтение и на запись. Это обеспечит более высокую скорость поиска, чем секционирование (партицирование), при котором ID секции хранится справа от UUID в том же идентификаторе (поле)

  • Сдвиги метки времени на значение, задаваемое аргументом функции uuidv7(timestamp_shift)

Цели

Максимальная производительность на запись и на чтение
Засекречивание времени создания записи

Секция RFC9562

6.1. Timestamp Considerations

Выдержки из RFC9562

Implementations MAY alter the actual timestamp. Some examples include security considerations around providing a real clock value within a UUID, to correct inaccurate clocks, to handle leap seconds, or instead of dividing a number of microseconds by 1000 to obtain a millisecond value; dividing by 1024 (or some other value) for performance reasons. This specification makes no requirement or guarantee about how close the clock value needs to be to the actual time

2. Наличие счетчика в UUID

Описание

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

Сейчас некоторые разработчики торопятся реализовать самые простые варианты структуры UUIDv7 без счетчика. У вариантов без счетчика ниже скорость поиска записей из-за нарушений упорядоченности массово сгенерированных UUIDv7 с одинаковым таймстемпом (меткой времени) миллисекундной точности. Если же применяется субмиллисекундный сегмент таймстемпа (если точность системных часов это позволяет), то он занимает больше битов, чем столь же емкий счетчик, оставляя меньше битов для случайной части.

Однако все продвинутые реализации UUIDv7 содержат счетчик:

Цели

Скорость поиска записей, созданных в результате массовой генерации записей с субмиллисекундными интервалами при недостаточной точности системных часов. Экономия места для энтропии по сравнению с субмиллисекундным сегментом метки времени. Лучшая устойчивость к коллизиям благодаря инициализации псевдослучайным числом по сравнению с субмиллисекундным сегментом временной метки

Секция RFC9562

6.2. Monotonicity and Counters

Выдержки из RFC9562

Batch UUID creation implementations MAY utilize a monotonic counter that increments for each UUID created during a given timestamp… With this method, the rand_a section (or a subset of its left-most bits) of UUIDv7 is used as fixed-length dedicated counter bits that are incremented for every UUID generation… In the event more counter bits are required, the most significant (left-most) bits of rand_b MAY be used as additional counter bits.

3. Защита от переполнения счетчика

Описание

Защита от переполнения счетчика в одном или обоих вариантах:

  • Инкремент метки времени (без инициализации счетчика) вплоть до момента, когда дата и время системных часов превысят текущее значение метки времени

  • Инициализируемый нулем левый бит счетчика при достаточной длине счетчика (более простой и поэтому предпочтительный вариант)

Цели

Защита от переполнения счетчика

Секция RFC9562

6.2. Monotonicity and Counters

Выдержки из RFC9562

The remaining most significant, left-most counter bit is initialized as zero for the sole purpose of guarding against counter rollovers.

4. Инициализация счетчика случайным числом

Описание

Инициализируемые случайным двоичным числом правые биты счетчика. Слишком длинный счетчик, инициализируемый случайным числом, может замедлить поиск записей. Поэтому между счетчиком и случайным сегментом, обновляемым для каждого UUIDv7, может быть вставлен сегмент, обновляемый случайным числом раз в миллисекунду. Этот сегмент уменьшит затраты на генерацию случайных чисел

Цели

Уникальность UUID

Секция RFC9562

6.2. Monotonicity and Counters

Выдержки из RFC9562

Implementations utilizing the fixed-length counter method randomly initialize the counter with each new timestamp tick… Implementations utilizing fixed-length counter method MAY also choose to randomly initialize a portion of the counter rather than the entire counter.

5. Индивидуальный расчет случайного числа для каждого UUID

Описание

Генерация нового случайного числа для каждого UUIDv7.
Функциональность необходима согласно RFC9562

Цели

Уникальность UUID. Неугадываемость UUID

Секция RFC9562

5.7. UUID Version 7

Выдержки из RFC9562

Random data for each new UUIDv7 generated

6. Опциональный сегмент составного идентификатора справа от UUID

Описание

Опциональный сегмент длиной 64 бита (из-за выравнивания данных) справа от UUID в ключевых столбцах БД с новым типом данных UUID_192. Опциональный сегмент UUID может содержать:

  • Обозначение (код) структуры опционального сегмента UUID

  • Идентификаторы модуля/микросервиса, таблицы-хаба/якоря, системы-источника, типа операции, типа сообщения, сегмента (shard) или секции (partition)

  • Сведения о переносе в архив или удалении

  • Контрольную сумму от UUID и опционального сегмента

Для соединения таблиц необходима новая функция, извлекающая, собственно, UUID (самые левые 128 битов) в формате UUID из столбца с типом данных UUID_192. Также необходима функция для быстрого парсинга опционального сегмента длиной 64 бита (без нерекомендуемого парсинга самого UUID)

Цели

Удобство разработки. Упрощение БД

Секция RFC9562

6.12. DBMS and Database Considerations

Выдержки из RFC9562

DBMS vendors are encouraged to provide functionality to generate and store UUID formats defined by this specification for use as identifiers or left parts of identifiers such as, but not limited to, primary keys, surrogate keys for temporal databases, foreign keys included in polymorphic relationships, and keys for key-value pairs in JSON columns and key-value databases.

7. Новый тип данных для автогенерации UUID

Описание

Создание нового условного (аналогично SERIAL в PostgreSQL) типа данных UUID_V7_GENERATOR:

CREATE TABLE имя_таблицы (имя_столбца UUID_V7_GENERATOR);

Эта инструкция должна обеспечивать запись в таблицу монотонно возрастающих сгенерированных значений формата UUIDv7, в том числе при параллельной записи в таблицу базы данных (как функция generateUUIDv7 в СУБД ClickHouse). Монотонность при многопоточности необходима, например, если нескольким микросервисам разрешено делать записи в общей таблице.
При этом значение UUIDv7 также присваивается новой встроенной функции uuid_v7_last (имя_таблицы) со значением скрытой переменной.

Цели

Удобство разработки. Уникальность UUID. Максимальная скорость записи и чтения

8. Избыточное удлинение счетчика

Описание

Избыточное удлинение счетчика (от необходимых 18 битов до 42), инициализируемого случайным числом каждую миллисекунду, как в реализации от LiosK (ссылка1, ссылка2), уменьшает необходимое количество энтропии, на генерацию которой нужно много вычислительных ресурсов. С другой стороны, удлинение счетчика может привести к снижению скорости поиска записей, так как два сгенерированных подряд UUID будут отличаться друг от друга битами, расположенными значительно правее. Поэтому необходим компромисс на основе бенчмарков, который будет разным для разных СУБД.

Цели

Максимальная скорость записи

9. Расчет ID секции по метке времени или при генерации метки времени

Описание

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

Цели

Максимальная скорость записи и чтения

10. Генерация UUID с последней известной меткой времени при сбое часов

Описание

Генерация UUID с последней известной меткой времени и инкрементом счетчика при временном отсутствии данных от системных часов

Цели

Отказоустойчивость

Секция RFC9562

6.1. Timestamp Considerations

Выдержки из RFC9562

Implementations MAY alter the actual timestamp. Some examples include security considerations around providing a real clock value within a UUID, to correct inaccurate clocks, to handle leap seconds, or instead of dividing a number of microseconds by 1000 to obtain a millisecond value; dividing by 1024 (or some other value) for performance reasons. This specification makes no requirement or guarantee about how close the clock value needs to be to the actual time.

11. Криптографически стойкий генератор псевдослучайных чисел

Описание

Криптографически стойкий генератор псевдослучайных чисел (CSPRNG), инициализируемый истинно случайными числами. Функциональность необходима согласно RFC9562

Цели

Уникальность UUID. Неугадываемость UUID

Секция RFC9562

6.8. Unguessability

Выдержки из RFC9562

Implementations SHOULD utilize a cryptographically secure pseudo-random number generator (CSPRNG) to provide values that are both difficult to predict («unguessable») and have a low likelihood of collision («unique»). The exception is when a suitable CSPRNG is unavailable in the execution environment. Take care to ensure the CSPRNG state is properly reseeded upon state changes, such as process forks, to ensure proper CSPRNG operation.

12. Оптимизация длины сегментов UUID подбором

Описание

Оптимизация длины сегментов UUIDv7 с помощью подбора и тестирования производительности.

Длина счетчика в разных реализациях UUIDv7 варьируется широко, выходя даже за установленные стандартом пределы. Видимо, нет смысла генерировать UUIDv7 чаще, чем СУБД сможет создавать записи, и это ограничивает необходимую длину счетчика. Однако ее можно установить и опытным путем: генерировать UUIDv7«ы для новых записей на самом быстром сервере на максимальной скорости, а потом бенчмарком замерять время поиска записей. Счетчик можно укорачивать, пока время поиска не станет расти из-за учащения его переполнений и вызванных этим нарушений упорядоченности. Потом можно добавить несколько бит к длине счетчика «на вырост», чтобы учесть возможное повышение производительности серверов. Главное тут не ошибиться с проектированием бенчмарка. Аналогичным образом можно определить опытным путем длину инициализируемого нулями левого сегмента счетчика, снижающего риск переполнения счетчика

Цели

Максимальная скорость записи и поиска/чтения

13.Функция, генерирующая ключ разбиения таблицы на секции (partitions) по таймстемпу (метке времени) UUIDv7

Описание

Приблизительно для каждого месяца создается секция. По UUIDv7 можно сразу вычислить, в какой именно секции его искать. Вот пример реализации

Ради производительности следует пренебречь точным соответствием календарным месяцам и даже более или менее точной длительности месячного интервала (28–31 день). Поэтому ключ разбиения таблицы на секции (partition) должен вычисляться как левая часть UUIDv7 определенной длины.

Цели

Максимальная скорость записи и поиска/чтения

14. Автоматическая миграция первичного ключа на UUID

Описание

Автоматическая миграция первичного ключа другого типа (bigint, char, text) на UUIDv7 или UUIDv7_192bit, обеспечивающая резервное копирование, сохранение имени ключевого поля, порядка записей, ссылочную целостность, историчность и версионность, замену автоинкремента генерацией, а также сохранение значений прежнего ключа в поле с модифицированным именем

Цели

Удобство разработки

15. Выделение столбцов формата UUID в интерфейсе

Описание

Фильтрация или выделение имен столбцов с типом данных UUID и UUID_192 цветом шрифта. Во многих случаях такие столбцы желательно сделать ключевыми.
Если есть индекс (по одному столбцу или составной): для светлой темы — выделение синим #0043ce rgb (0,67,206), а для темной темы — голубым #85b1ff rgb (133,177,255).
Если нет индекса: для светлой темы — выделение оранжево-красным #f34900 rgb (243,73,0), а для темной темы — оранжево-розовым #ff9766 rgb (255,151,102). Во многих случаях для таких столбцов желательно создать индекс. Эти цвета подходят при дальтонизме и контрастны

Цели

Удобство разработки. Реклама UUID

16. Поддержка кодировок, включая Crockford’s Base32

Описание

Поддержка кодировок UUID:

  • Двоичный код (binary) (для внутреннего представления в полях БД),

  • Crockford’s Base32 по образцу 01HAH3×2SC85B88HZ82JT5V278 (сверх нового стандарта) — человекочитаемый и легко копируемый формат,

  • Каноническое представление UUID по образцу 018b163d-0b83–7ba0-b837-da575d0ff824 (для обеспечения обратной совместимости)

Для обмена данными с внешними информационными системами может использоваться любая из этих кодировок

Цели

Удобство разработки

Секция RFC9562

  1. UUID Format

Выдержки из RFC9562

UUIDs MAY be represented as binary data or integers. When in use with URNs or as text in applications, any given UUID SHOULD be represented by the «hex-and-dash» string format consisting of multiple groups of upper or lowercase alphanumeric hexadecimal characters separated by single dashes/hyphens.

17. Пакетный онлайн генератор UUIDv7

Описание

Пакетный онлайн генератор UUIDv7, в том числе с доступом по API

Цели

Удобство разработки. Реклама UUID

18. Сквозной поиск заданных значений UUID

Описание

Облегченный поиск (по всей БД или схеме, в том числе с помощью функции SQL и по API) таблиц и записей, содержащих заданное значение формата UUID или UUID_192, в том числе в полях формата JSON/jsonb, ARRAY и т.п.

Цели

Удобство разработки

19. Сквозной поиск связанных или одноименных полей формата UUID

Описание

Облегченный поиск (по всей БД или схеме, в том числе с помощью функции SQL и по API) связанных или одноименных полей формата UUID или UUID_192 в других таблицах и представлениях. Возможность одновременного переименования таких полей

Цели

Удобство разработки

20. Копирование и вставка UUID одним щелчком мыши

Описание

Копирование значения формата UUID и UUID_192 одним левым щелчком мыши (без необходимости предварительного выделения) и вставка (без форматирования) одним правым щелчком мыши в подходящее место. Контекстное меню и сочетания клавиш при этом не используются

Цели

Удобство разработки

21. Автоматическое слияние дубликатов UUID

Описание

Автоматическая замена в БД, с сохранением истории, нескольких UUIDv7 на один новый UUIDv7, если выяснилось, что прежние UUIDv7 обозначают один и тот же объект

Цели

Удобство разработки

22. Приспособление СУБД к UUID-центричным БД

Описание

Добавление в СУБД специальных быстрых алгоритмов вставки, индексирования и поиска записей для UUIDv7, с учетом монотонности с возможными нарушениями

Цели

Максимальная скорость записи и поиска/чтения

23. Защита от перевода часов назад

Описание

Сохранение (с последующим увеличением) значения метки времени при переводе системных часов назад. Функциональность необходима согласно RFC9562.

Цели

Скорость поиска записей, созданных в период перевода системных часов назад. Отказоустойчивость

Секция RFC9562

6.1. Timestamp Considerations

Выдержки из RFC9562

if it is possible for the system clock to move backward due to either manual adjustment or corrections from a time synchronization protocol, implementations need to determine how to handle such cases… This specification makes no requirement or guarantee about how close the clock value needs to be to the actual time.

24. Буферизация (частей) UUID для максимальной производительности

Описание

Генерация и буферизация сегментов UUID или целых UUID заранее

Цели

Производительность

Секция RFC9562

6.3. UUID Generator States

Выдержки из RFC9562

This stable storage MAY be used to record various portions of the UUID generation which prove useful for batch UUID generation purposes and monotonic error checking with UUIDv6 and UUIDv7. These stored values include but are not limited to last known timestamp, clock sequence, counters, and random data.

25. Исключение дополнительных секунд

Описание

Исключение дополнительных секунд. Функциональность необходима согласно RFC9562

Цели

Скорость поиска записей, созданных в период возникновения дополнительных секунд

Секция RFC9562

5.7. UUID Version 7

Выдержки из RFC9562

UUID version 7 features a time-ordered value field derived from the widely implemented and well known Unix Epoch timestamp source, the number of milliseconds since midnight 1 Jan 1970 UTC, leap seconds excluded.

26. Маскировка даты создания записи

Описание

Периодический случайный сдвиг диапазона значений метки времени

Цели

Неугадываемость UUID. Защита от раскрытия даты и времени создания записи

Секция RFC9562

6.1. Timestamp Considerations

Выдержки из RFC9562

Implementations MAY alter the actual timestamp. Some examples include security considerations around providing a real clock value within a UUID, to correct inaccurate clocks, to handle leap seconds, or instead of dividing a number of microseconds by 1000 to obtain a millisecond value; dividing by 1024 (or some other value) for performance reasons. This specification makes no requirement or guarantee about how close the clock value needs to be to the actual time.

27. Настроечные данные генератора UUID

Описание

Сохранение настроек генератора UUID в именованном формате JSON отдельно от кода программы генератора UUID

Цели

Удобство разработки

28. Автоматическая нормализация и денормализация данных с использованием UUID ключей

Описание

Разработка синтаксиса SQL (вместо Python и Excel) для массового автоматического создания в хранилищах данных таблиц и запросов, реализующих типовые процессы по нормализации входных данных и денормализации детальных данных. Необходимо применение методологий Anchor Modeling, Bitemporal modeling и Data Vault и использование UUIDv7 в качестве ключей. Сейчас для автоматизации построения схемы данных в соответствии с методологией Anchor Modeling применяются Python и Excel, что не очень удобно и довольно трудоемко

Цели

Удобство разработки

29. UUID идентиконы в гипертексте

Описание

Автоматическое отображение UUID (а также содержащих UUID полей БД и гиперссылок без текста) в гипертексте (на веб-страницах, в электронных документах, в программном коде в IDE и т.д.) в виде стилизованных идентиконов с соответствующими контекстными меню. Идентикон одного и того же UUID (или гиперссылка с UUID) должен во всех документах, программах и платформах выглядеть одинаково, но идентиконы незначительно отличающихся UUID должны иметь большие визуальные отличия. При этом должны сохраниться возможности поиска UUID по тексту, копирования UUID и т.д. Контекстное меню должно содержать индикатор совпадения UUID (или гиперссылки) с сохраненным в буфере обмена, а также пункт поиска таких же UUID по тексту.
Альтернативный вариант — показывать только последние 4 символа UUID и кнопку для отображения остальной части UUID

Цели

Удобство разработки и эксплуатации информационных систем

30. Всплывающая подсказка (tooltip) к UUID в гипертексте

Описание

Всплывающая подсказка (tooltip) к UUID в гипертексте, содержащая человекочитаемое название (не код)

Цели

Удобство разработки и эксплуатации информационных систем

31. UUID метки в электронных документах, файлах и строках БД

Описание

Автоматическая фильтрация и поиск электронных документов (json, xml, гипертекст), строк БД и файлов по содержащимся в них UUID с необходимыми тегами разметки, в указанных столбцах таблицы БД или с необходимыми URL

Цели

Удобство разработки и эксплуатации информационных систем

32. Автоматический поиск первичных и внешних ключей среди полей с типом UUID и построение ER-диаграммы

Описание

Наличие в СУБД PostgreSQL типа UUID сужает область поиска и позволяет автоматизировать поиск первичных и внешних ключей среди полей с типом UUID и построение ER-диаграммы по содержащимся в полях данным при отсутствии хорошей документации. Однако это имеет смысл только при преимущественном использовании полей с типом UUID в качестве ключей БД

Цели

Удобство разработки

© Habrahabr.ru