UUID v7 Generator
Generate time-ordered UUID v7 for database primary keys
…
Форматировать
Что такое UUID v7?
UUID v7 — это UUID-формат нового поколения, стандартизированный в RFC 9562 (май 2024). Он кодирует 48-битную Unix-временную метку в миллисекундах в наиболее значимых битах, а оставшиеся биты заполняются криптографически защищёнными случайными данными.
Поскольку временная метка занимает старшие биты, значения UUID v7 сортируются хронологически — как лексикографически, так и численно. Это делает их отличным выбором для первичных ключей баз данных, где случайные UUID (v4) вызывают фрагментацию B-tree-индекса.
Почему случайные UUID фрагментируют индексы баз данных
B-tree-индексы — используемые PostgreSQL, MySQL, SQLite и большинством других баз данных — хранят строки отсортированными по значению ключа. При вставке новой строки база данных должна разместить её в правильной позиции в дереве индекса.
При UUID v4 (полностью случайном) каждая новая вставка попадает в случайную позицию. Это заставляет базу данных постоянно читать и перезаписывать страницы индекса, создавая фрагментированный, раздутый индекс.
Битовая структура UUID v7
UUID v7 имеет ширину 128 бит и делится на шесть полей:
| Биты | Поле | Назначение |
|---|---|---|
| 48 | unix_ts_ms | 48-битная Unix-временная метка в миллисекундах — занимает всю старшую половину |
| 4 | ver | Номер версии — всегда 0111 (десятичное 7) |
| 12 | rand_a | 12 бит криптографически защищённых случайных данных |
| 2 | var | Маркер варианта — всегда 10 (вариант RFC 4122) |
| 62 | rand_b | 62 бита криптографически защищённых случайных данных |
Комбинация временной метки + случайных данных даёт 74 бита энтропии в пределах каждой миллисекунды — достаточно для тысяч уникальных UUID без коллизий.
UUID v7 против UUID v1
Оба UUID v7 и v1 кодируют временную метку, но их подходы принципиально различны:
| Характеристика | UUID v7 | UUID v1 |
|---|---|---|
| Эпоха | Unix (1 января 1970) | Gregorian (15 октября 1582) |
| Точность времени | Миллисекунды | 100 наносекунд |
| Лексикографически сортируемый | Да | Нет |
| Конфиденциальность | Не раскрывает MAC | Раскрывает MAC-адрес хоста |
| Производительность индекса БД | Отличная (монотонная вставка) | Плохая (случайные позиции) |
| Стандарт | RFC 9562 (2024) | RFC 4122 (2005) |
| Поддержка в браузере | Нет нативного API | Нет нативного API |
Для новых проектов предпочтительнее UUID v7. Единственная причина использовать v1 — совместимость с существующими системами.
UUID v7 против ULID
UUID v7 и ULID решают одну и ту же проблему — сортируемые уникальные ID — но с разными компромиссами:
- <strong>UUID v7</strong>: Стандарт IETF (RFC 9562), 128-битный, нативная поддержка в любой системе, понимающей UUID.
- <strong>UUID v7</strong>: Монотонный в пределах одной миллисекунды при правильной реализации, 122 бита энтропии.
- <strong>UUID v7</strong>: Менее компактный — 36 символов с дефисами или 32 без них.
- <strong>UUID v7</strong>: Нет нативного API в браузере; требует библиотеку.
- <strong>ULID</strong>: Спецификация сообщества, широко используется. 26 символов в Base32-кодировке Crockford.
- <strong>ULID</strong>: Более компактный и читаемый. URL-безопасный, нечувствителен к регистру.
- <strong>ULID</strong>: 128 бит, но другая байтовая схема — не совместим с форматом UUID.
- <strong>ULID</strong>: Монотонный режим доступен в большинстве реализаций.
Используйте UUID v7 для совместимости с UUID. Используйте ULID, если компактность важнее совместимости.
Поддержка баз данных
Поддержка нативного UUID v7 добавляется в базы данных:
pg_uuidv7.BINARY(16) или VARCHAR(36). Нет нативного типа UUID v7.TEXT или BLOB. Поддержка на уровне приложения.Примеры кода
UUID v7 пока не поддерживается нативно в браузерах или Node.js. Используйте библиотеку:
function generateUuidV7() {
const buf = new Uint8Array(16)
crypto.getRandomValues(buf)
const ms = BigInt(Date.now())
// Embed 48-bit Unix ms timestamp
buf[0] = Number((ms >> 40n) & 0xFFn)
buf[1] = Number((ms >> 32n) & 0xFFn)
buf[2] = Number((ms >> 24n) & 0xFFn)
buf[3] = Number((ms >> 16n) & 0xFFn)
buf[4] = Number((ms >> 8n) & 0xFFn)
buf[5] = Number(ms & 0xFFn)
// Set version 7 (0111xxxx)
buf[6] = (buf[6] & 0x0F) | 0x70
// Set variant (10xxxxxx)
buf[8] = (buf[8] & 0x3F) | 0x80
const hex = [...buf].map(b => b.toString(16).padStart(2, '0')).join('')
return `${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20)}`
}
// Node.js 20+ built-in
// import { randomUUID } from 'node:crypto' // v4 only — no v7 yet in stdlib# pip install uuid7 import uuid_extensions uid = uuid_extensions.uuid7() print(uid) # e.g. 018e2b3d-1a2b-7000-8000-abc123456789 print(uid.time) # Unix ms timestamp embedded in the UUID # Or as a plain string from uuid_extensions import uuid7str print(uuid7str())
-- PostgreSQL 13+ extension-free implementation
CREATE OR REPLACE FUNCTION uuid_generate_v7()
RETURNS uuid
LANGUAGE sql
AS $$
SELECT encode(
set_bit(
set_bit(
overlay(
uuid_send(gen_random_uuid())
PLACING substring(int8send(floor(extract(epoch FROM clock_timestamp()) * 1000)::bigint) FROM 3)
FROM 1 FOR 6
),
52, 1
),
53, 1
),
'hex'
)::uuid;
$$;
-- Usage as a default primary key
CREATE TABLE events (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7(),
payload jsonb,
created_at timestamptz DEFAULT now()
);function extractTimestamp(uuid: string): Date {
const hex = uuid.replace(/-/g, '')
const ms = parseInt(hex.slice(0, 12), 16) // first 48 bits = ms timestamp
return new Date(ms)
}
const uid = '018e2b3d-1a2b-7000-8000-abc123456789'
console.log(extractTimestamp(uid).toISOString())
// → "2024-03-15T10:22:05.259Z"