UUID v7 Generator

Generate time-ordered UUID v7 for database primary keys

Formátovat

Počet:

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í:

BityPoleÚčel
48unix_ts_ms48bitové Unix časové razítko v milisekundách — zaujímá celou vysokou polovinu
4verČíslo verze — vždy 0111 (desetinně 7)
12rand_a12 bitů kryptograficky bezpečných náhodných dat
2varZnačka varianty — vždy 10 (varianta RFC 4122)
62rand_b62 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:

FunkceUUID v7UUID v1
Epocha / Časová základnaUnix epocha (1. jan. 1970)Gregoriánská epocha (15. říj. 1582)
Časová přesnost1 milisekunda100 nanosekund
SeřaditelnéAno — k-seřaditelné by designNe — časová pole jsou v rozložení UUID zamíchána
SoukromíŽádné informace o hostiteli neuniklyObsahuje MAC adresu generujícího hostitele
Výkon DB indexuVýborný — sekvenční vkládání, minimální fragmentaceŠpatný — nesekvenční přes časové razítko
StandardRFC 9562 (2024)RFC 4122 (2005)
Nativní podpora prohlížečeZatí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í:

UUID v7
  • 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
ULID
  • 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í:

PostgreSQL
PostgreSQL: uložte do sloupce uuid. Rozšíření pg-uuidv7 přidává serverovou funkci uuid_generate_v7(), pokud potřebujete ID generovaná DB.
MySQL / MariaDB
MySQL / MariaDB: uložte do sloupce 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.
SQLite
SQLite: uložte jako 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:

JavaScript (browser / Node.js 20+)
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
Python (uuid7 library)
# 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 — generate UUID v7
-- 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()
);
TypeScript — extract timestamp from UUID v7
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"

Časté dotazy

Je UUID v7 zpětně kompatibilní s UUID v4?
Ano. UUID v7 používá stejný 128bitový, 32hexadecimální číslicový, formát drátu s pomlčkami jako všechny ostatní verze UUID. Jakýkoliv systém, který ukládá nebo přenáší UUID, přijme UUID v7 bez změn. Nibble verze (7) a variantní bity ho identifikují jako v7 pro nástroje, které kontrolují strukturu UUID.
Prozrazuje UUID v7 časové razítko generování?
Ano — prvních 48 bitů je Unix millisekudové časové razítko, takže kdokoliv s UUID může přibližně určit, kdy bylo vygenerováno (na nejbližší milisekundu). Pokud je prozrazení doby vytvoření problémem pro váš případ použití, použijte místo toho UUID v4.
Mohu použít UUID v7 jako primární klíč databáze bez samostatného sloupce created_at?
Ano. Protože UUID v7 obsahuje časové razítko s přesností na milisekundy, můžete tuto hodnotu dekódovat, abyste získali přibližný čas vytvoření. Nicméně pro přehlednost a indexovatelnost mnoho týmů stále uchovává explicitní sloupec created_at a používá UUID v7 pouze pro sloupec ID.
Kolik entropie má UUID v7?
UUID v7 má 74 bitů náhodných dat (12 bitů v rand_a + 62 bitů v rand_b). To je o něco méně než 122 bitů UUID v4, ale stále poskytuje astronomicky velký prostor bez kolize pro praktické použití. Snížená náhodnost je kompromisem za získání seřaditelnosti časovými razítky.
Je UUID v7 nativně podporováno v prohlížečích nebo Node.js?
Zatím ne na počátku roku 2025. Standard RFC 9562 byl zveřejněn v květnu 2024 a podpora platforem stále dohání. Prozatím použijte balíček npm uuidv7. Nativní podpora přes crypto.randomUUID{ version: 7 } nebo podobná API může přijít v budoucích verzích prohlížečů a Node.js.