UUID v7 Generator
Generate time-ordered UUID v7 for database primary keys
…
Formátovat
Co je UUID v7?
UUID v7 je formát UUID nové generace standardizovaný v RFC 9562 (květen 2024). Kóduje 48bitové Unix millisekudové časové razítko do nejvýznamnějších bitů, následované značkami verze a varianty, a zbývající bity vyplňuje kryptograficky bezpečnými náhodnými daty.
Protože časové razítko zaujímá vysoké bity, hodnoty UUID v7 se třídí chronologicky — jak lexikograficky, tak numericky. To z nich činí vynikající volbu pro primární klíče databáze, kde náhodná UUID (v4) způsobují fragmentaci B-tree indexu.
Proč náhodná UUID fragmentují databázové indexy
B-tree indexy — používané PostgreSQL, MySQL, SQLite a většinou ostatních databází — uchovávají řádky seřazené podle hodnoty klíče. Při vložení nového řádku musí databáze umístit ho na správnou seřazenou pozici v indexu.
S UUID v4 (plně náhodné) každé nové vložení přistane na v podstatě náhodné pozici ve stromu indexu. To nutí databázi neustále číst a přepisovat vnitřní stránky indexu, rozdělovat plné stránky a nechávat jiné napůl prázdné. Výsledkem je fragmentovaný, nadutý index, který zpomaluje jak zápisy, tak čtení, jak tabulka roste.
Rozložení bitů UUID v7
UUID v7 je 128 bitů wide, rozděleno do šesti polí:
| Bity | Pole | Účel |
|---|---|---|
| 48 | unix_ts_ms | 48bitové Unix časové razítko v milisekundách — zaujímá celou vysokou polovinu |
| 4 | ver | Číslo verze — vždy 0111 (desetinně 7) |
| 12 | rand_a | 12 bitů kryptograficky bezpečných náhodných dat |
| 2 | var | Značka varianty — vždy 10 (varianta RFC 4122) |
| 62 | rand_b | 62 bitů kryptograficky bezpečných náhodných dat |
Přesnost časového razítka je 1 milisekunda. V rámci stejné milisekundy jsou hodnoty UUID v7 seřazeny podle jejich náhodné přípony — není zaručeno, že jsou monotónně rostoucí pod milisekunder, ale jsou k-seřaditelné: ID generovaná blízko sebe v čase se seřadí blízko sebe v indexu.
UUID v7 vs UUID v1
UUID v1 i UUID v7 obsahují časové razítko, ale výrazně se liší v návrhu:
| Funkce | UUID v7 | UUID v1 |
|---|---|---|
| Epocha / Časová základna | Unix epocha (1. jan. 1970) | Gregoriánská epocha (15. říj. 1582) |
| Časová přesnost | 1 milisekunda | 100 nanosekund |
| Seřaditelné | Ano — k-seřaditelné by design | Ne — časová pole jsou v rozložení UUID zamíchána |
| Soukromí | Žádné informace o hostiteli neunikly | Obsahuje MAC adresu generujícího hostitele |
| Výkon DB indexu | Výborný — sekvenční vkládání, minimální fragmentace | Špatný — nesekvenční přes časové razítko |
| Standard | RFC 9562 (2024) | RFC 4122 (2005) |
| Nativní podpora prohlížeče | Zatím ne (žádné crypto.randomUUID v7) | Není nativně dostupné |
Pro jakýkoliv nový projekt, který potřebuje časově uspořádaná UUID, preferujte UUID v7 před UUID v1. UUID v1 je starší a prozrazuje informace o hostiteli.
UUID v7 vs ULID
ULID (Universally Unique Lexicographically Sortable Identifier) řeší podobný problém jako UUID v7. Zde je srovnání:
- Sleduje standard UUID RFC 9562 — kompatibilní se všemi nástroji UUID
- Hex formát s pomlčkami — univerzálně rozpoznávaný
- Nativní podpora sloupců UUID v databázi
- 128 bitů celkem
- Kódování Crockford Base32 — 26 znaků, mírně kompaktnější
- Nerozlišuje velikost písmen a vyhýbá se nejednoznačným znakům (I, L, O, U)
- Na první pohled čitelnější pro člověka
- Vyžaduje knihovnu — žádná nativní podpora platformy
Pokud jste již v ekosystému UUID (sloupec uuid v PostgreSQL, REST API vracející UUID), použijte UUID v7. Pokud začínáte od nuly a preferujete přívětivější kódování pro člověka, ULID je rozumná alternativa.
Používání UUID v7 v databázích
UUID v7 zatím většina databází nativně negeneruje, ale může být uloženo ve standardních sloupcích UUID a generováno v kódu aplikace nebo prostřednictvím rozšíření:
uuid. Rozšíření pg-uuidv7 přidává serverovou funkci uuid_generate_v7(), pokud potřebujete ID generovaná DB.BINARY(16) nebo CHAR(36). Generujte v kódu aplikace. MySQL 8.0+ má podporu uspořádaného UUID přes UUID_TO_BIN(UUID(), 1) pro v1, ale v7 vyžaduje generování na úrovni aplikace.TEXT (36 znaků) nebo BLOB (16 bajtů). Generujte v kódu aplikace. Lexikografické třídění na TEXT funguje správně, protože UUID v7 používá předponu časového razítka s pevnou šířkou.Příklady kódu
UUID v7 zatím není dostupné přes crypto.randomUUID(). Použijte knihovnu jako uuidv7 (npm), dokud nedorazí nativní podpora:
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"