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 | 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 символів у кодуванні Crockford Base32.
- <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"