Komplexní srovnání UUID v4, UUID v7 a ULID: jak vyvážit časovou návaznost, efektivitu a čitelnost
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ě:
- Generátor UUIDv4 (rozhraní zatím v japonštině a angličtině)
- Generátor UUIDv7 (rozhraní zatím v japonštině a angličtině)
- Čtení časových údajů z UUIDv7 (rozhraní zatím v japonštině a angličtině)
- Generátor ULID (rozhraní zatím v japonštině a angličtině)
- Čtení časových údajů z ULID (rozhraní zatím v japonštině a angličtině)
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 typemUUID
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 nauuid
, pořadí se zachová. - ULID: použijte
char(26)
/text
nebobytea(16)
(nutný převod z/do Base32).
- v4/v7: nejlépe funguje typ
- 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)
neboBINARY(16)
+ převod.
- SQLite
- Nemá vlastní typ
uuid
. PoužijteBLOB(16)
neboTEXT
; indexovaný BLOB je relativně efektivní.
- Nemá vlastní typ
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í.