Generator UUID v7

Generuje posortowane czasowo UUID v7 dla kluczy głównych bazy danych

Formatuj

Liczba:

Czym jest UUID v7?

UUID v7 to format UUID nowej generacji standaryzowany w RFC 9562 (maj 2024). Koduje 48-bitowy uniksowy znacznik czasu w milisekundach w bitach o największym znaczeniu, a następnie znaczniki wersji i wariantu, a pozostałe bity wypełnia kryptograficznie bezpiecznymi losowymi danymi.

Ponieważ znacznik czasu zajmuje wysokie bity, wartości UUID v7 sortują się chronologicznie — zarówno leksykograficznie, jak i numerycznie. Dzięki temu doskonale nadają się do kluczy głównych bazy danych, gdzie losowe UUID (v4) powodują fragmentację indeksu B-tree.

Dlaczego losowe UUID fragmentują indeksy bazy danych

Indeksy B-tree — używane przez PostgreSQL, MySQL, SQLite i większość innych baz danych — przechowują wiersze posortowane według wartości klucza. Podczas wstawiania nowego wiersza baza danych musi umieścić go na właściwej posortowanej pozycji w indeksie.

W przypadku UUID v4 (w pełni losowego) każde nowe wstawianie trafia do zasadniczo losowej pozycji w drzewie indeksów. Zmusza to bazę danych do ciągłego odczytywania i przepisywania wewnętrznych stron indeksu, dzielenia pełnych stron i pozostawiania innych do połowy pustych. Wynikiem jest pofragmentowany, nadmiernie rozrośnięty indeks, który spowalnia zarówno zapisy, jak i odczyty w miarę wzrostu tabeli.

Układ bitów UUID v7

UUID v7 ma 128 bitów szerokości, podzielonych na sześć pól:

BityPoleCel
48unix_ts_ms48-bitowy uniksowy znacznik czasu w milisekundach — zajmuje całą górną połowę
4verNumer wersji — zawsze 0111 (dziesiętnie 7)
12rand_a12 bitów kryptograficznie bezpiecznych losowych danych
2varZnacznik wariantu — zawsze 10 (wariant RFC 4122)
62rand_b62 bity kryptograficznie bezpiecznych losowych danych

Precyzja znacznika czasu wynosi 1 milisekundę. W obrębie tej samej milisekundy wartości UUID v7 są uporządkowane według losowego sufiksu — nie ma gwarancji monotonicznego wzrostu poniżej milisekundy, ale są k-sortowalne: identyfikatory generowane blisko siebie w czasie będą sortowały się blisko siebie w indeksie.

UUID v7 a UUID v1

Zarówno UUID v1, jak i UUID v7 zawierają znacznik czasu, ale znacznie różnią się projektem:

CechaUUID v7UUID v1
Epoka / Podstawa czasuEpoka uniksowa (1 stycznia 1970)Epoka gregoriańska (15 października 1582)
Precyzja czasu1 milisekunda100 nanosekund
SortowalnyTak — k-sortowalny z założeniaNie — pola czasu są pomieszane w układzie UUID
PrywatnośćBrak wycieków informacji o hościeZawiera adres MAC generującego hosta
Wydajność indeksu bazy danychDoskonała — sekwencyjne wstawiania, minimalna fragmentacjaSłaba — niesequencyjne mimo znacznika czasu
StandardRFC 9562 (2024)RFC 4122 (2005)
Natywna obsługa przeglądarkiJeszcze nie (brak crypto.randomUUID v7)Niedostępne natywnie

W przypadku każdego nowego projektu wymagającego UUID posortowanych według czasu, preferuj UUID v7 nad UUID v1. UUID v1 jest przestarzały i ujawnia informacje o hoście.

UUID v7 a ULID

ULID (Universally Unique Lexicographically Sortable Identifier) rozwiązuje podobny problem co UUID v7. Oto porównanie:

UUID v7
  • Zgodny ze standardem UUID RFC 9562 — kompatybilny ze wszystkimi narzędziami UUID
  • Format szesnastkowy z myślnikami — powszechnie rozpoznawany
  • Natywna obsługa kolumn UUID w bazach danych
  • 128 bitów łącznie
ULID
  • Kodowanie Crockford Base32 — 26 znaków, nieco bardziej kompaktowe
  • Bez rozróżniania wielkości liter, bez niejednoznacznych znaków (I, L, O, U)
  • Bardziej czytelny dla człowieka na pierwszy rzut oka
  • Wymaga biblioteki — brak natywnej obsługi platformy

Jeśli jesteś już w ekosystemie UUID (kolumna uuid PostgreSQL, REST API zwracające UUID), użyj UUID v7. Jeśli zaczynasz od nowa i wolisz bardziej przyjazne dla człowieka kodowanie, ULID jest rozsądną alternatywą.

Używanie UUID v7 w bazach danych

UUID v7 nie jest jeszcze natywnie generowany przez większość baz danych, ale może być przechowywany w standardowych kolumnach UUID i generowany w kodzie aplikacji lub przez rozszerzenia:

PostgreSQL
PostgreSQL: przechowuj w kolumnie uuid. Rozszerzenie pg-uuidv7 dodaje funkcję po stronie serwera uuid_generate_v7(), jeśli potrzebujesz identyfikatorów generowanych przez bazę danych.
MySQL / MariaDB
MySQL / MariaDB: przechowuj w kolumnie BINARY(16) lub CHAR(36). Generuj w kodzie aplikacji. MySQL 8.0+ obsługuje posortowane UUID za pomocą UUID_TO_BIN(UUID(), 1) dla v1, ale v7 wymaga generowania na poziomie aplikacji.
SQLite
SQLite: przechowuj jako TEXT (36 znaków) lub BLOB (16 bajtów). Generuj w kodzie aplikacji. Leksykograficzne sortowanie na TEXT działa poprawnie, ponieważ UUID v7 używa prefiksu znacznika czasu o stałej szerokości.

Przykłady kodu

UUID v7 nie jest jeszcze dostępny przez crypto.randomUUID(). Używaj biblioteki takiej jak uuidv7 (npm) do czasu pojawienia się natywnej obsługi:

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"

Często zadawane pytania

Czy UUID v7 jest wstecznie kompatybilny z UUID v4?
Tak. UUID v7 używa tego samego formatu 128-bitowego, 32-cyfrowego szesnastkowego z myślnikami co wszystkie inne wersje UUID. Każdy system, który przechowuje lub transmituje UUID, zaakceptuje UUID v7 bez zmian. Nibble wersji (7) i bity wariantu identyfikują go jako v7 dla narzędzi sprawdzających strukturę UUID.
Czy UUID v7 ujawnia znacznik czasu generowania?
Tak — pierwsze 48 bitów to uniksowy znacznik czasu w milisekundach, więc każdy posiadający UUID może określić w przybliżeniu, kiedy został wygenerowany (z dokładnością do milisekundy). Jeśli ujawnianie czasu tworzenia jest problemem dla twojego przypadku użycia, użyj zamiast tego UUID v4.
Czy mogę użyć UUID v7 jako klucza głównego bazy danych bez osobnej kolumny created_at?
Tak. Ponieważ UUID v7 zawiera znacznik czasu z dokładnością do milisekundy, możesz zdekodować tę wartość, aby uzyskać przybliżony czas tworzenia. Jednak dla przejrzystości i możliwości indeksowania wiele zespołów nadal zachowuje jawną kolumnę created_at i używa UUID v7 tylko dla kolumny identyfikatora.
Ile entropii ma UUID v7?
UUID v7 ma 74 bity losowych danych (12 bitów w rand_a + 62 bity w rand_b). Jest to nieco mniej niż 122 bity UUID v4, ale nadal zapewnia astronomicznie dużą przestrzeń wolną od kolizji do praktycznego użycia. Zmniejszona losowość jest kompromisem za uzyskanie sortowalności według znacznika czasu.
Czy UUID v7 jest natywnie obsługiwany w przeglądarkach lub Node.js?
Jeszcze nie na początku 2025 roku. Standard RFC 9562 został opublikowany w maju 2024, a obsługa platform nadal nadrabia. Używaj pakietu npm uuidv7 na razie. Natywna obsługa przez crypto.randomUUID({ version: 7 }) lub podobne API może pojawić się w przyszłych wersjach przeglądarek i Node.js.