Преимущества функции uuidv7()
В 18 версии PostgreSQL появится функция uuidv7(). Она разработана для замены последовательных автоинкрементных идентификаторов SERIAL, BIGSERIAL и IDENTITY, которые могут привести к катастрофическому дублированию ключей при слиянии данных, и для замены более медленных UUIDv4.
Функция uuidv7() в PostgreSQL имеет следующие преимущества:
Безопасное слияние данных из нескольких источников без необходимости перегенерации ключей
Генерация ключей одновременно в нескольких процессах без ущерба для уникальности и порядка ключей
Возможность искажения даты и времени создания записи для конфиденциальной информации (в отличие от почти всех других аналогичных функций)
Функция uuidv7() также полезна для приложений, требующих хронологического извлечения данных, таких как системы логирования и базы данных временных рядов.
Таким образом, использование функции uuidv7() позволит упростить архитектуру информационных систем, упростить SQL-запросы, избежать некоторых ошибок, облегчить внесение изменений и благодаря этому повысить надежность и снизить стоимость разработки и сопровождения информационных систем.
Необходимость официального бенчмарка
Иногда встречаются бездоказательные утверждения, что использование идентификаторов UUIDv7 вместо автоинкремента якобы замедляет вставку записей в таблицы баз данных. Чтобы такого рода утверждения не влияли на выбор типа идентификатора, нужен официальный бенчмарк.
Кроме того, такой бенчмарк должен облегчить выбор типа идентификатора с учетом оборудования, задач и условий конкретного клиента.
Еще одна причина для разработки официального бенчмарка – это абсурдно низкое качество самодельных бенчмарков.
Например, часто функции генерации идентификаторов сравниваются и оцениваются по темпу генерации. Но это лишено всякого смысла, так как в любом случае идентификаторы генерируются гораздо быстрее, чем в них возникает потребность – из-за относительно низкого темпа создания записей в таблицах баз данных.
Другие самодельные бенчмарки доказывают и без того очевидный факт, что UUIDv7 гораздо быстрее, чем UUIDv4.
Некоторые без бенчмарков сравнивают только длину UUIDv7 и целочисленных идентификаторов, не обращая внимания на остальные обстоятельства. Но «скупой платит дважды» (см. выше пункт «Преимущества uuidv7()»).
На самом деле критически важен темп однопоточной и многопоточной вставки записей (в том числе, с учетом партиционирования) с ключами, сгенерированными функцией uuidv7() – по сравнению с автоинкрементом. Но для некоторых клиентов могут оказаться важными и другие параметры, в частности, скорость работы SQL-запросов различных типов.
Кроме того, наличие официального бенчмарка вообще избавило бы клиентов от необходимости разработки самодельных бенчмарков. Ведь в абсолютном большинстве случаев клиенты нуждаются в одних и тех же сценариях работы бенчмарков, а различия в потребностях могут быть учтены изменением значений входных параметров (количество строк в таблице, объем строки).
Хорошо было бы включить бенчмарк в 18 версию PostgreSQL, отдельно от патча для собственно функции uuidv7(). Время для этого еще есть, так как заморозка фич произойдет в апреле 2025 года.
Входные параметры бенчмарка
Бенчмарк должен запускаться как функция с одним обязательным параметром «тип идентификатора» (по умолчанию «uuidv7() без намеренного сдвига таймстемпа») и двумя опциональными параметрами:
Количество записей в тестовой таблице, по умолчанию 1 миллион
Объем данных в поле типа BYTEA, по умолчанию 2048 байтов
Шаги бенчмарка
До запуска шагов бенчмарка создается тестовая таблица mock_table:
CREATE TABLE mock_table (id UUID PRIMARY KEY DEFAULT uuidv7(), payload BYTEA);
На каждом своем шаге бенчмарк должен выполнять SQL-запросы различных типов, поскольку именно это в конечном итоге требуется клиентам, выбирающим тип идентификатора. Однако в бенчмарке не нужно, чтобы запросы выводили какие-то осмысленные результаты, а важно лишь время исполнения запросов.
INSERT INTO mock_table (id, payload) VALUES (uuidv7(), (decode(repeat('FF', payload_size_b), 'hex'))); --здесь вместо uuidv7() может быть подставлена другая функция, генерирующая идентификаторы
Параллельная (многопоточная) вставка записей. Это не выразить на SQL
SELECT COUNT(*) FROM mock_table a LEFT JOIN mock_table b ON b.id = a.id WHERE b.id IS NULL;
SELECT COUNT(*) FROM mock_table a INNER JOIN mock_table b ON b.id = a.id WHERE b.id IS NULL;
SELECT id, COUNT(*) FROM mock_table GROUP BY id HAVING COUNT(*) > 1;
DELETE FROM mock_table a USING mock_table b WHERE b.id = a.id;
Таблица результатов бенчмарка
Таблица результатов запущенного бенчмарка должна содержать одну строку значений для каждого шага бенчмарка, в том числе:
Дата и время (в формате по умолчанию) запуска бенчмарка
Значения каждого входного параметра (в том числе, взятые по умолчанию)
Имя шага бенчмарка (1_insert, 2_parallel_insert, 3_left_join, 4_inner_join, 5_group_by, 6_delete)
Темп обработки записей, штук в миллисекунду
Использование CPU, %
Использование памяти, МБ
Использование диска, МБ/с
Сравниваемые типы идентификаторов
Помимо UUIDv7 и BIGSERIAL имеет смысл запускать бенчмарк по меньшей мере с такими типами идентификаторов по выбору пользователя: UUIDv4, ULID, Snowflake ID.
Комментарии (2)
vmalyutin
11.01.2025 10:37И ещё. Объясните мне, ещё не читавшему официальную документацию, каким образом функция генерирующая чего-то там не как старая последовательно поможет временным рядам и особо хронологическому чтению, плиз.
vmalyutin
Честно говоря ничего не понял. Во-первых, бенчмарк у pg есть. Не помню, как он называется, но он есть. Во-вторых, если вам надо проверить производительность вашего конкретного сценария, то почему бы вам его и не проверить? В конце концов не будут же разработчики pg делать отдельный бенчмарк на каждый тип данных в PG!?