UUID a ULID jsou mechanismy, které v distribuovaných systémech a databázích přidělují „globálně unikátní“ identifikátory. Na první pohled vypadají podobně, avšak liší se mírou standardizace, efektivitou uložení, čitelností pro člověka i rizikem, že odhalí doprovodné informace. V tomto článku porovnávám tři nejpoužívanější formáty — UUID v4, UUID v7 a ULID — a na závěr doplňuji stručné poznámky k UUID v1 / v6.

Veškeré generování i zpětné čtení časových údajů si můžete vyzkoušet přímo v prohlížeči prostřednictvím těchto nástrojů. Vše běží lokálně:


UUID v4: čistě náhodný formát

UUID v4 věnuje ze 128 bitů 122 bitům náhodě, takže pravděpodobnost kolize je extrémně malá. Nevýhodou je, že nedrží časovou posloupnost, takže například B‑stromové indexy musí často dělit stránky a zhoršuje se lokálnost zápisů. Formát se hodí pro session ID, jednorázové tokeny a všude tam, kde „náhodnost“ představuje hlavní požadavek.


UUID v7: chronologický nástupce

UUID v7 bylo standardizováno v roce 2024 v rámci RFC 9562. Prvních 48 bitů nese čas UNIX epochy v milisekundách, zbytek se vyplňuje náhodně. Abecední pořadí (porovnání řetězců) se tím rovná pořadí generování, takže jako primární klíč si drží dobrou lokálnost v indexu. Zachovává i kompatibilitu se stávajícími typy uuid a BINARY(16).

  • Rozpočet náhody: po odečtení bitů pro verzi a variantu zůstává zhruba 74 bitů náhody. Některé implementace ve stejném milisekundovém okamžiku provádějí monotónní úpravy, aby se vyhnuly kolizím, nicméně entropie je pro praxi víc než dostatečná.
  • Riziko odhalení informací: z ID lze odhadnout čas vytvoření s přesností na milisekundy. Pokud je generátor náhod slabý a objem vydávaných ID velký, může být snazší hádat sousední identifikátory. V interních systémech je to obvykle přijatelné, u veřejných endpointů může být potřeba riziko vyhodnotit.

ULID: ID přívětivé pro člověka

ULID byl představen v roce 2016 jako de facto standard. Používá Crockford Base32 s 26 znaky, přičemž vynechává znaky, které se snadno pletou (O/I/L/1), takže se hodí pro vložení do URL i pro kopírování. Stejně jako v7 ukládá v prvních 48 bitech čas (ms), takže řetězce lze řadit abecedně a odpovídá to chronologii.

  • Záruka řazení: Base32 řetězce ULID lze lexikograficky porovnávat a výsledkem je vždy časové pořadí (široce podporovaná je i monotónní varianta).
  • Způsob uložení: TEXT(26) nabízí dobrou čitelnost, ale pro binární uložení v databázi je nutné převádět mezi Base32 a 16 bajty. S typem UUID je často nekompatibilní.
  • Riziko odhalení informací: podobně jako u v7 je z ID vidět čas (ms). Pokud nechcete, aby odběratelé API snadno odhadovali pořadí vydaných zdrojů, zvažte alternativu.

Srovnávací tabulka (praktický pohled)

Aspekt UUID v4 UUID v7 ULID
Standardizace RFC 4122 RFC 9562 (2024) De facto, bez formální standardizace
Bitové rozložení 128 bitů, 122 bitů náhodných 48 bitů = UNIX ms + zbytek náhodných (volitelně monotónní) 48 bitů = UNIX ms + 80 bitů náhodných
Entropie náhody ≈122 bitů ≈74 bitů (po odečtení verze/varianty) 80 bitů
Textová podoba 36 znaků (hex + pomlčky) 36 znaků (hex + pomlčky) 26 znaků (Crockford Base32)
Přirozené řazení (text) ✗ (náhodné) ✓ (lexikograficky = chronologicky) ✓ (lexikograficky = chronologicky)
Přirozené řazení (binárně) ✓ (BINARY(16) vzestupně dle memcmp) ✓ (pokud je 6B čas v čele v big-endian formátu, memcmp odpovídá času)
Vlastnosti uložení v DB Ideálně uuid / BINARY(16) Ideálně uuid / BINARY(16) TEXT(26) je čitelné, BINARY(16) vyžaduje převod / typ UUID bývá nekompatibilní
Lokálnost v indexu Slabá (náhodné vkládání láme stránky) Dobrá (sekvenční přidávání, pozor na hotspoty) Dobrá (sekvenční přidávání, pozor na hotspoty)
Cena porovnání pro rovnost Nízká (porovnání 16 B) Nízká (porovnání 16 B) TEXT je dražší (kolace), BINARY levné
Náročnost kódování Hex ↔ 16 bajtů (lehká) Hex ↔ 16 bajtů (lehká) Base32 ↔ 16 bajtů (náročnější)
Čitelnost pro člověka / URL Nízká Nízká Vysoká
Odhalení času Žádné Ano (ms) Ano (ms)
Typické použití Session ID, jednorázové tokeny Primární klíče, eventy, logy v čase URL a veřejná ID, čitelné logy

Poznámka k hotspotům u v7/ULID: pokud se velký objem zápisů soustředí na jediný shard či leader, může se konec B‑stromu přehřívat. Jako mitigaci zvažte drobné promíchání vyšších bitů („prefix shuffle“) nebo kombinaci s klíčem pro sharding.


Poznámky k implementaci v DB (PostgreSQL / MySQL / SQLite)

  • PostgreSQL
    • v4/v7: nejlépe funguje typ uuid. U v7 stačí vložit řetězec a převést na uuid, pořadí se zachová.
    • ULID: použijte char(26)/text nebo bytea(16) (nutný převod z/do Base32).
  • MySQL / InnoDB
    • BINARY(16) je rychlé a úsporné (v4/v7). U v7 zůstává memcmp v big-endian pořadí časově správně.
    • ULID: CHAR(26) nebo BINARY(16) + převod.
  • SQLite
    • Nemá vlastní typ uuid. Použijte BLOB(16) nebo TEXT; indexovaný BLOB je relativně efektivní.

Bezpečnostní pohled

  • Odolnost proti hádání: u v7/ULID se časové bity opakují, takže pokud ve stejné milisekundě vydáváte mnoho ID a RNG je slabé, zvyšuje se riziko odhadu sousedních hodnot. Využívejte kryptograficky bezpečný PRNG a minimalizujte případné zkreslení při monotónní generaci.
  • Únik metadat: z ID lze odhadnout čas vydání a přibližný objem. U veřejných API je vhodné oddělit interní a externí ID.

Shrnutí: jak vybírat

  • UUID v4: čistá náhoda. Neseřaditelné, teoreticky nenulové riziko kolizí. Hodí se pro session ID, CSRF tokeny apod.
  • UUID v7: standardizované, časově seřaditelné UUID. Nejlepší volba pro interní systémy a databázové klíče. Monotónní generace snižuje praktické riziko kolizí.
  • ULID: (relativně) nejpřátelštější pro člověka. Silný kandidát pro URL nebo logy, pokud potřebujete čitelnost, i když pro interní primární klíče obvykle vítězí v7. Monotónní generace drží kolize na uzdě.

Doplněk: UUID v1 / v6 ve zkratce

  • UUID v1 (čas + MAC) Obsahuje 60bitový časový údaj (100 ns) a identifikátor uzlu (často MAC adresu). Má silnou časovou posloupnost, ale hrozí odhalení MAC adresy a implementační úskalí při zpětném posunu hodin. Z důvodů soukromí se dnes běžně nedoporučuje.

  • UUID v6 (přeuspořádaná v1) Překládá časovou část v1 do big-endian pořadí, aby šlo snadněji řadit. Historicky šlo o „lépe řaditelné v1“, ale standardizace se sjednotila na v7. U nových návrhů se proto zpravidla volí právě v7.


Kontrolní seznam pro implementaci

  • Používejte kryptograficky bezpečný PRNG poskytovaný operačním systémem.
  • Monotónní generaci v7/ULID nastavte tak, aby se vyhnula kolizím ve stejné milisekundě a zároveň nestrannila náhodu.
  • Preferujte uuid / BINARY(16) a vyhněte se výkonovým ztrátám kvůli kolacím na textových sloupcích.
  • Stanovte politiku pro externí ID (zda smí odhalovat pořadí) a případně oddělte interní a veřejné identifikátory.

V praxi se jako výchozí volba pro interní primární klíče nabízí UUID v7, zatímco ULID stojí za zvážení v situacích, kdy je důležitější čitelnost nebo existují technická omezení. Jen pokud máte zvláštní požadavky (např. zákaz odhalení času), dává smysl vrátit se k v4 nebo jinému řešení.