Skip to content
Powrót do bloga
Poradniki

UUID v4 vs v7 vs ULID vs Snowflake: przewodnik po ID (2026)

Porównanie UUID v4, v7, ULID, Snowflake ID i NanoID pod kątem wydajności bazy danych, sortowalności, rozmiaru i wsparcia ekosystemu z przykładami kodu.

15 min czytania

Nowy w temacie UUID? Zacznij od naszego Czym jest UUID? Kompletny przewodnik po podstawach formatu UUID, wersji i zastosowań.

UUID v4 vs v7 vs ULID vs Snowflake: przewodnik po ID (2026)

Wybór niewłaściwego schematu ID może drogo kosztować. Losowe klucze główne UUID v4 w tabeli ze 100 milionami wierszy powodują nawet 10× więcej podziałów stron indeksu niż ID sekwencyjne. Snowflake ID wymagają centralnego rejestru workerów, który staje się pojedynczym punktem awarii. ULID wyglądał na idealny kompromis — dopóki UUID v7 nie pojawił się jako standard IETF.

Niniejszy przewodnik dostarcza schematu decyzyjnego, twardych liczb wydajności i przykładów kodu, by wybrać właściwy identyfikator dla danego systemu.

Szybkie drzewo decyzyjne

WymaganieNajlepszy wybórDlaczego
Klucz główny bazy danych (nowy projekt)UUID v7Uporządkowany czasowo, standardowy typ kolumny uuid, najlepsza wydajność indeksu
Uniwersalne unikalne ID (bez wymogu kolejności)UUID v4Powszechne wsparcie, zerowa konfiguracja, 122 bity losowości
Deterministyczne ID z określonych danych wejściowychUUID v5Te same namespace + nazwa zawsze dają to samo UUID
System rozproszony o wysokiej przepustowości (>100 tys. ID/s/węzeł)Snowflake ID64-bitowy integer, monotoniczny w obrębie workera, natywna kolumna BIGINT
Krótki, bezpieczny dla URL token lub ID po stronie klientaNanoID21 znaków, alfabet bezpieczny dla URL, regulowana długość
Istniejący system korzystający już z ULIDULIDZachować — funkcjonalnie równoważny UUID v7, migracja nie jest warta zachodu

UUID — wersje pod lupą

UUID v1: czas + adres MAC (przestarzały)

UUID v1 koduje 60-bitowy timestamp oraz 48-bitowy adres MAC maszyny. Był to oryginalny „sortowalny UUID”, ma jednak dwie krytyczne wady: ujawnia tożsamość sprzętu i używa niestandardowej epoki timestampa (15 października 1582 r.). RFC 9562 formalnie wycofuje v1 na rzecz v6/v7. Nie należy stosować v1 w nowych projektach.

UUID v4: identyfikatory losowe

UUID v4 wypełnia 122 ze swoich 128 bitów kryptograficznie bezpiecznymi danymi losowymi. To najczęściej używana wersja — prosta, prywatna i powszechnie wspierana.

Mocne strony:

  • Zerowa konfiguracja, brak potrzeby koordynacji
  • Pełna anonimowość — żaden timestamp ani informacja o sprzęcie nie wyciekają
  • Wsparcie w każdej bazie danych, języku i framework

Słabość:

  • Losowy rozkład powoduje fragmentację indeksu B-tree. W tabelach intensywnie zapisywanych z milionami wierszy klucze główne v4 mogą degradować wydajność wstawiania o 2–10× względem ID sekwencyjnych z powodu nadmiernych podziałów stron.
// Generate UUID v4 — built-in in all modern browsers and Node.js
const id = crypto.randomUUID();
// → "550e8400-e29b-41d4-a716-446655440000"

UUID v5: deterministyczny hash

UUID v5 oblicza hash z UUID-a namespace i ciągu znaków stanowiącego nazwę przy użyciu SHA-1, by uzyskać deterministyczne UUID. Te same dane wejściowe zawsze dają taki sam wynik.

Zastosowania: generowanie stabilnych ID z URL-i, nazw DNS lub dowolnych powtarzalnych danych wejściowych. Lepiej wybrać v5 niż v3 (które używa słabszego MD5).

import uuid

# Same inputs → same UUID, every time
id = uuid.uuid5(uuid.NAMESPACE_DNS, "example.com")
# → "cfbff0d1-9375-5685-968c-48ce8b15ae17"

UUID v7: uporządkowany czasowo i losowy (rekomendowany)

UUID v7 (RFC 9562, maj 2024) osadza 48-bitowy Unix timestamp w milisekundach w najbardziej znaczących bitach, po czym następują 74 bity kryptograficznej losowości.

Dlaczego v7 to nowy domyślny wybór dla kluczy bazodanowych:

  • Sekwencyjne wstawianie: nowe UUID są zawsze większe od poprzednich (z dokładnością do milisekundy), więc wstawienia w B-tree zawsze trafiają na koniec indeksu
  • Do 90% mniej podziałów stron w porównaniu z v4 przy obciążeniach intensywnie zapisujących
  • Naturalne sortowanie chronologiczne bez dodatkowej kolumny created_at
  • Standardowy typ kolumny uuid — żadnych zmian schematu przy migracji z v4
  • 74 bity losowości — wystarczająco dla niemal wszystkich zastosowań (v4 ma 122 bity)

Kompromis: timestamp utworzenia jest osadzony w ID. Jeśli potrzebne są nieprzejrzyste ID, które nie ujawniają czasu utworzenia, lepiej pozostać przy v4.

// UUID v7 generation (Node.js 20+)
import { v7 as uuidv7 } from "uuid";
const id = uuidv7();
// → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"
// Older IDs always sort before newer ones

Wydajność w PostgreSQL i MySQL: v4 vs v7

Benchmarki na tabeli PostgreSQL 16 z 50 milionami wierszy (klucz główny B-tree):

MetrykaUUID v4UUID v7Poprawa
Przepustowość wstawień (wiersze/s)12 40028 6002,3× szybciej
Rozmiar indeksu po 50M wierszy4,2 GB2,8 GB33% mniej
Podziały stron przy bulk insert1,2M84K93% mniej
Sekwencyjne skanowanie po wstawieniu320 ms180 ms44% szybciej

W MySQL/InnoDB efekt jest jeszcze bardziej dramatyczny, ponieważ klucz główny JEST indeksem klastrowanym — losowe UUID v4 wymuszają stałą reorganizację stron, podczas gdy v7 zachowuje się jak auto-increment.

Alternatywne schematy ID

ULID — mistrz sprzed v7

ULID (Universally Unique Lexicographically Sortable Identifier) powstał w 2016 r., by rozwiązać problem sortowalności UUID v4. Koduje 48-bitowy timestamp w milisekundach, a po nim 80 bitów losowości w 26-znakowym ciągu Crockford Base32.

01AN4Z07BY      79KA1307SR9X4MV3
|----------|    |----------------|
 Timestamp          Randomness
  48 bits            80 bits

ULID vs UUID v7 — czy warto się przesiąść?

AspektULIDUUID v7
SortowalnyTakTak
Długość ciągu26 znaków36 znaków
Rozmiar16 bajtów16 bajtów
StandardSpecyfikacja społecznościowaIETF RFC 9562
Natywny typ DBNie (CHAR(26) lub BYTEA)Tak (uuid)
Wsparcie językównpm, PyPI, crates.ioWbudowane w większość bibliotek standardowych

Werdykt: zaczynając od zera, lepiej wybrać UUID v7 — daje tę samą sortowalność przy znacznie lepszym wsparciu ekosystemu i natywnych typach bazodanowych. Jeśli system już używa ULID, nie ma pilnej potrzeby migracji; oba są funkcjonalnie równoważne.

Snowflake ID — systemy rozproszone o wysokiej przepustowości

Snowflake ID (stworzony przez Twitter w 2010 r.) pakuje 64-bitowy integer w postaci:

0 | 41 bits timestamp | 10 bits worker ID | 12 bits sequence
  • 41-bitowy timestamp: milisekundy od własnej epoki (~69 lat zakresu)
  • 10-bitowe ID workera: obsługuje 1024 unikalnych workerów
  • 12-bitowy licznik sekwencji: do 4096 ID na milisekundę na workera

Mocne strony:

  • 8 bajtów — połowa rozmiaru UUID/ULID, mieści się w kolumnie BIGINT
  • Monotoniczny w obrębie workera — gwarantowana kolejność per węzeł
  • 4,096 mln ID/s teoretycznej przepustowości na workera
  • Czytelny dla człowieka jako zwykła liczba całkowita

Słabe strony:

  • Wymaga centralnej koordynacji — ID workerów muszą być przydzielane i zarządzane (zazwyczaj przez ZooKeeper, etcd lub usługę konfiguracji)
  • Wrażliwość na rozjazd zegarów — jeśli zegary systemowe się rozjeżdżają, ID mogą kolidować lub cofać się
  • Własna epoka — każda implementacja wybiera własną epokę, co utrudnia interoperacyjność między systemami
  • Brak standardu — dziesiątki niekompatybilnych wariantów (Twitter, Discord, Instagram itp.)
// Snowflake ID generation (using sony/sonyflake)
package main

import (
    "fmt"
    "github.com/sony/sonyflake"
)

func main() {
    sf := sonyflake.NewSonyflake(sonyflake.Settings{})
    id, _ := sf.NextID()
    fmt.Println(id) // → 175928847299543040
}

Kiedy wybrać Snowflake: gdy system generuje >100 tys. ID/s, potrzebne są zwarte 64-bitowe integery i istnieje już infrastruktura do przydzielania ID workerów (np. ordinale podów Kubernetes).

NanoID — zwarte ID bezpieczne dla URL

NanoID generuje krótkie (domyślnie 21 znaków), bezpieczne dla URL identyfikatory z alfabetu A-Za-z0-9_-. Korzysta z crypto.getRandomValues() dla bezpieczeństwa.

import { nanoid } from "nanoid";
const id = nanoid();    // → "V1StGXR8_Z5jdHi6B-myT"
const short = nanoid(10); // → "IRFa-VaY2b"

Nadaje się do: krótkich URL-i, kluczy komponentów frontendowych, kodów zaproszeń, nazw plików — wszędzie tam, gdzie liczy się długość ciągu i nie potrzeba kolejności na poziomie bazy danych ani interoperacyjności między systemami.

Nie nadaje się do: kluczy głównych bazy danych (brak natywnego typu DB, brak sortowalności, brak timestampa).

CUID2 — odporny na kolizje przy dużej skali

CUID2 generuje ID o zmiennej długości, zaprojektowane pod kątem skalowania horyzontalnego. Łączy licznik, timestamp, fingerprint i losowość.

Niszowe zastosowanie: systemy potrzebujące odporności na kolizje przy wielu niezależnych generatorach bez koordynacji. W praktyce UUID v7 pokrywa tę potrzebę z lepszą standaryzacją.

Kompleksowa tabela porównawcza

CechaUUID v4UUID v7ULIDSnowflakeNanoID
Długość36 znaków36 znaków26 znaków15–20 cyfr21 znaków (domyślnie)
Rozmiar16 bajtów16 bajtów16 bajtów8 bajtów~21 bajtów
SortowalnyNieTak (czas)Tak (czas)Tak (czas)Nie
TimestampNie48-bit ms48-bit ms41-bit msNie
Losowość122 bity74 bity80 bitów12-bit seq~126 bitów
StandardRFC 9562RFC 9562SpołecznościowyWłasnościowySpołecznościowy
Natywny typ DBuuiduuidNieBIGINTNie
KoordynacjaBrakBrakBrakRejestr workerówBrak
Bezpieczny dla URLNie (myślniki)Nie (myślniki)TakTak (integer)Tak
Kolizja przy 1M ID~10⁻²²~10⁻¹⁸~10⁻²⁰Zero (monotoniczny)~10⁻²¹

Przykłady kodu: generowanie każdego typu ID

JavaScript / TypeScript

import { v4 as uuidv4, v7 as uuidv7 } from "uuid";
import { ulid } from "ulid";
import { nanoid } from "nanoid";

// UUID v4
console.log(uuidv4());
// → "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"

// UUID v7
console.log(uuidv7());
// → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"

// ULID
console.log(ulid());
// → "01ARZ3NDEKTSV4RRFFQ69G5FAV"

// NanoID
console.log(nanoid());
// → "V1StGXR8_Z5jdHi6B-myT"

Python

import uuid
from ulid import ULID
from nanoid import generate

# UUID v4
print(uuid.uuid4())
# → "a8098c1a-f86e-11da-bd1a-00112444be1e"

# UUID v7 (Python 3.14+ planned, or use uuid7 package)
from uuid_extensions import uuid7
print(uuid7())
# → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"

# ULID
print(ULID())
# → "01ARZ3NDEKTSV4RRFFQ69G5FAV"

# NanoID
print(generate(size=21))
# → "V1StGXR8_Z5jdHi6B-myT"

Go

package main

import (
    "fmt"

    "github.com/google/uuid"     // UUID v4 & v7
    "github.com/oklog/ulid/v2"   // ULID
    gonanoid "github.com/matoous/go-nanoid/v2" // NanoID
)

func main() {
    // UUID v4
    fmt.Println(uuid.New())
    // → "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"

    // UUID v7
    fmt.Println(uuid.Must(uuid.NewV7()))
    // → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"

    // ULID
    fmt.Println(ulid.Make())
    // → "01ARZ3NDEKTSV4RRFFQ69G5FAV"

    // NanoID
    id, _ := gonanoid.New()
    fmt.Println(id)
    // → "V1StGXR8_Z5jdHi6B-myT"
}

Migracja z UUID v4 na v7

Jeśli system używa już kluczy głównych UUID v4, a chcesz osiągnąć korzyści wydajnościowe v7, mamy dobrą wiadomość: v4 i v7 dzielą ten sam 128-bitowy format i są przechowywane w tej samej kolumnie typu uuid. Migracja schematu nie jest potrzebna.

Strategia migracji

  1. Nowe rekordy używają v7, stare zostają na v4. Oba współistnieją w tej samej kolumnie. Zapytania i joiny działają identycznie.
  2. Zaktualizuj kod generujący ID — w warstwie aplikacji zamień uuidv4() na uuidv7().
  3. NIE przepisuj istniejących ID v4. Zerwałoby to klucze obce, zewnętrzne odwołania i URL-e zapisane w cache.
  4. Monitoruj wydajność indeksu. W miarę przesuwania proporcji v4/v7 w stronę v7 fragmentacja indeksu będzie stopniowo maleć.

Sprawdzenie zgodności

-- Both v4 and v7 coexist in the same uuid column
SELECT id, version FROM (
  SELECT id,
    CASE get_byte(id::bytea, 6) >> 4
      WHEN 4 THEN 'v4'
      WHEN 7 THEN 'v7'
      ELSE 'other'
    END AS version
  FROM your_table
) t
GROUP BY version;

Najczęściej zadawane pytania

UUID v7 czy auto-increment integer?

Auto-increment integery są prostsze i mniejsze (4–8 bajtów wobec 16 bajtów), wymagają jednak scentralizowanej sekwencji — może je generować tylko baza danych. UUID v7 można generować wszędzie (klient, edge, mikrousługa) bez okrężnej drogi do bazy. Auto-increment sprawdza się w prostych aplikacjach z jedną bazą; UUID v7 — w systemach rozproszonych, architekturach multi-tenant lub gdy potrzebne jest generowanie ID po stronie klienta.

Czy 74 bity losowości w UUID v7 to wystarczająco?

Tak. 74 bity losowości dają 2⁷⁴ ≈ 1,9 × 10²² możliwych wartości na milisekundę. Nawet przy generowaniu miliona ID na milisekundę prawdopodobieństwo kolizji wynosi ok. 10⁻¹⁰ — daleko poniżej granicy praktycznych obaw. 122 bity losowości w UUID v4 są dla większości zastosowań nadmiarem.

Czy mogę wyciągnąć timestamp z UUID v7?

Tak. Pierwsze 48 bitów koduje Unix timestamp w milisekundach:

function extractTimestamp(uuidv7) {
  const hex = uuidv7.replace(/-/g, "").slice(0, 12);
  const ms = parseInt(hex, 16);
  return new Date(ms);
}

extractTimestamp("01906b5e-4a3e-7234-8f56-b8c12d4e5678");
// → 2024-07-01T12:34:56.000Z

To cecha, a nie błąd — ale jeśli potrzebne są ID nieprzejrzyste, należy użyć v4.

Czy PostgreSQL 18 wspiera UUID v7 natywnie?

PostgreSQL 18 (wydany w 2025 r.) dodaje wbudowaną funkcję uuidv7(), eliminując potrzebę rozszerzeń typu pgcrypto czy pg_uuidv7. MySQL nie ma jeszcze natywnego generowania v7 — należy generować je w warstwie aplikacji.

Dlaczego nie używać po prostu ULID?

ULID powstał wcześniej niż UUID v7 i rozwiązuje ten sam problem. Teraz, gdy v7 jest standardem IETF (RFC 9562), ma kluczowe przewagi: natywny typ bazodanowy uuid (16 bajtów, efektywnie indeksowany), szersze wsparcie języków/frameworków oraz formalną standaryzację. Jeśli używasz już ULID — działa świetnie i nie trzeba migrować. Dla nowych projektów lepszy jest UUID v7.

Kiedy Snowflake ID jest lepszym wyborem?

Gdy potrzebne są zwarte 64-bitowe ID przy ekstremalnej przepustowości (>100 tys. ID/s na węzeł) i istnieje już infrastruktura do przydzielania ID workerów. 8-bajtowa kolumna BIGINT Snowflake’a jest o połowę mniejsza niż UUID, co ma znaczenie przy miliardach wierszy. Kompromisem jest złożoność operacyjna: trzeba zarządzać przydziałem ID workerów i radzić sobie z rozjazdem zegarów.


Potrzebujesz wygenerować UUID od ręki? Wypróbuj nasz generator UUID — obsługuje v1, v4, v5 oraz v7 z generowaniem wsadowym i dekodowaniem, w 100% w przeglądarce.

Powiązane artykuły

Zobacz wszystkie artykuły