Budowa UUID v7: znacznik czasu i losowość w jednym identyfikatorze
Generator UUID v7 tworzy identyfikatory, które łączą w sobie dwa elementy: 48‑bitowy znacznik czasu Unix wyrażony w milisekundach oraz 74 bity losowe (przy czym 2 bity są zarezerwowane na oznaczenie wersji i wariantu). Łącznie daje to 128 bitów, które w standardowej reprezentacji tekstowej przyjmują postać 36 znaków, np. 018f3a6e-7b2c-7d5e-8f1a-2b3c4d5e6f7g. Pierwsze 12 znaków (po odrzuceniu myślników) koduje znacznik czasu w systemie szesnastkowym. Dzięki temu same pierwsze znaki identyfikatora mówią, kiedy został on wygenerowany – co ma zasadnicze znaczenie dla wydajności baz danych.
Identyfikatory są generowane lokalnie w przeglądarce przy użyciu silnych mechanizmów losowości dostępnych w nowoczesnych API JavaScript (crypto.getRandomValues). Żadne dane nie są wysyłane na serwer BroBroGo, co chroni prywatność użytkownika i eliminuje opóźnienia sieciowe. Każda zmiana dowolnego parametru – liczby identyfikatorów, użycia wielkich liter lub obecności myślników – powoduje natychmiastowe ponowne wygenerowanie całego zestawu.
Dlaczego sortowalność czasowa ma znaczenie dla indeksów baz danych
Tradycyjne identyfikatory UUID w wersji 4 są losowe w każdym bicie. Gdy takie ID staje się kluczem głównym w tabeli z indeksem B‑drzewa, każdy nowy rekord trafia w losowe miejsce w strukturze indeksu. Powoduje to częste dzielenie stron, fragmentację indeksu i spadek wydajności przy zapisie – zwłaszcza przy dużej liczbie transakcji na sekundę.
UUID w wersji 7 rozwiązuje ten problem, umieszczając znacznik czasu na początku identyfikatora. Nowo wygenerowane ID mają podobne prefiksy czasowe, więc trafiają do tych samych lub sąsiednich stron indeksu. B‑drzewo utrzymuje lepszą lokalność, zmniejsza się liczba operacji wejścia‑wyjścia, a wstawianie jest przewidywalnie szybsze. To sprawia, że UUID v7 jest naturalnym wyborem dla systemów, w których klucz główny musi być losowy (np. ze względów bezpieczeństwa), ale wydajność zapisu jest krytyczna.
Należy jednak pamiętać o ograniczeniu: UUID v7 nie gwarantuje ścisłego uporządkowania identyfikatorów wygenerowanych w tej samej milisekundzie. W obrębie jednej milisekundy losowa część decyduje o kolejności. Jeśli dwie transakcje zostaną utworzone w tym samym czasie, ID nie muszą być ułożone rosnąco. Dla większości zastosowań – np. w event source'ingu, dziennikach audytowych czy kolejkach wiadomości – przybliżone sortowanie jest w pełni wystarczające.
UUID v7 a UUID v4 – porównanie cech
| Cecha | UUID v4 | UUID v7 |
|---|---|---|
| Łączna liczba bitów | 128 (122 losowe, 6 wersja/wariant) | 128 (48 znacznik czasu, 74 losowe, 6 wersja/wariant) |
| Sortowalność względem czasu | Brak | Chronologiczna (w granicach milisekund) |
| Długość tekstowa | 36 znaków | 36 znaków |
| Nieodgadnialność | Bardzo wysoka | Wysoka – znacznik czasu jest jawny |
| Ryzyko kolizji przy dużej skali | Niskie, czysto losowe | Niskie, ale zależne od rozdzielczości czasowej |
| Wpływ na indeks B-drzewa | Fragmentacja | Lokalność, mniejsza fragmentacja |
Podstawowa różnica leży w strukturze binarnej. UUID v4 ma 122 bity losowe – do wygenerowania go wystarczy dobry generator liczb losowych. UUID v7 wymaga dostępu do dokładnego zegara systemowego. Dlatego implementacje muszą uwzględniać możliwość cofnięcia czasu (np. po synchronizacji NTP) i odpowiednio dostosować znacznik – zwykle przez dodanie licznika monotonicznego. Narzędzie na tej stronie generuje każdy identyfikator niezależnie, bez stanu między generacjami, więc nie próbuje korygować dryfu zegara – przyjmuje bieżący timestamp i uzupełnia go losowością.
Znaczenie myślników i wielkości liter w identyfikatorach
Standardowy format UUID, zdefiniowany w RFC 9562, przewiduje 36 znaków z myślnikami w pozycjach 8–4–4–12 – czyli po 8, a następnie po 4 znakach w każdej grupie. Myślniki pełnią rolę separatora ułatwiającego odczyt, ale są zbędne dla maszyn. Wiele systemów przechowuje UUID bez myślników (32 znaki szesnastkowe) lub używa formatu binarnego (16 bajtów). Przełącznik „Include hyphens” pozwala dostosować output do wymagań konkretnego systemu – jeśli aplikacja oczekuje surowego ciągu 32 znaków, wystarczy wyłączyć tę opcję.
Podobnie wielkość liter ma znaczenie, choćby ze względów konwencji. Większość implementacji domyślnie podaje UUID małymi literami, ale niektóre bazy danych, interfejsy API lub narzędzia (np. Java UUID.toString()) stosują wielkie litery. Przełącznik „Uppercase” wymusza reprezentację szesnastkową wyłącznie wielkimi literami (A-F). Należy pamiętać, że identyfikatory porównywane są niezależnie od wielkości liter – schema walidacji powinna to uwzględniać. Zmiana wielkości liter nie wpływa na wartość binarną, ale może ujawnić preferencje kodowania.
Zastosowania wymagające chronologicznie sortowalnych identyfikatorów
Trzy główne obszary, w których UUID v7 sprawdza się szczególnie dobrze, to:
- Systemy rozproszone – węzły mogą generować identyfikatory niezależnie, bez komunikacji między sobą, a mimo to będą one w przybliżeniu uporządkowane według czasu utworzenia. To kluczowe przy łączeniu strumieni zdarzeń z różnych źródeł.
- Dzienniki audytowe i event sourcing – każdy zdarzeniu nadawany jest znacznik czasu wbudowany w identyfikator. Można sortować zdarzenia bez osobnego pola timestamp, co redukuje rozmiar indeksów.
- Kolejki wiadomości i systemy przesyłania komunikatów – jeśli wiadomości są identyfikowane przez UUID v7, odbiorca może je sortować w kolejności nadawania bez ryzyka poważnych przetasowań (w granicach tej samej milisekundy).
W każdej z tych dziedzin często stosowano wcześniej UUID v4, płacąc cenę w postaci gorszej wydajności indeksów. Zastąpienie go wersją 7 jest prostą zmianą – format identyfikatora pozostaje 36‑znakowy, nie wymaga modyfikacji schematu bazy danych, a jedynie zmiany generatora po stronie aplikacji.
Ograniczenia i niuanse przy generowaniu wielu identyfikatorów
Generator na tej stronie może wyprodukować od 1 do 100 identyfikatorów jednocześnie. Ponieważ wszystkie są generowane w pętli w obrębie tego samego wywołania, istnieje duże prawdopodobieństwo, że ich znaczniki czasu będą identyczne – zwłaszcza gdy kod działa szybko. W efekcie powstałe ID różnią się tylko losową częścią, a ich kolejność w liście nie odpowiada żadnemu porządkowi wewnątrz milisekundy. To nie jest błąd, lecz cecha wersji 7.
Dla użytkownika oznacza to, że jeśli potrzebuje ściśle rosnących identyfikatorów w skali poniżej milisekundy, UUID v7 nie wystarczy – trzeba sięgnąć po ULID z licznikiem monotonicznym lub własny generator z sekwencyjnym numerem. Jednak w praktyce większość baz danych operuje na poziomie milisekund, a same B‑drzewa i tak radzą sobie dobrze, gdy identyfikatory są uporządkowane w skali czasu – nawet jeśli w obrębie milisekundy pojawią się przetasowania.
Kolejny niuans: znacznik czasu w UUID v7 to milisekundy od epoki Unix (1 stycznia 1970). Jeśli system zostanie przestawiony w przeszłość, identyfikatory mogą mieć nielogiczną kolejność. W narzędziu lokalnym nie ma mechanizmu zapobiegającego cofnięciu zegara – każdy identyfikator powstaje na podstawie bieżącego czasu w momencie kliknięcia.
Często zadawane pytania (FAQ)
Czy UUID v7 może być używany jako klucz główny w istniejącej tabeli używającej UUID v4? Tak. Format pozostaje taki sam – 36 znaków, 128 bitów. Możesz generować nowe rekordy z UUID v7, a stare pozostaną w UUID v4. Indeks B‑drzewa będzie działać poprawnie, choć nowsze rekordy będą grupować się w przedziale czasowym, nieco zmieniając rozkład indeksu.
Czy to narzędzie gwarantuje brak kolizji? Nie w sensie matematycznym. UUID v7 ma 74 bity losowe, czyli około 1,9 × 10^22 możliwych losowych części. Kolizja przy 100 wygenerowanych ID jest praktycznie niemożliwa – ale nie niemożliwa. Dla porównania, ryzyko kolizji UUID v4 (122 bity losowe) jest jeszcze niższe. W praktyce obie wersje są bezpieczne w standardowych zastosowaniach.
Dlaczego warto użyć UUID v7 zamiast sekwencyjnego licznika (np. AUTO_INCREMENT)? Sekwencyjny licznik ujawnia liczbę rekordów i pozwala odgadnąć następny identyfikator. UUID v7 jest nieprzewidywalny (losowa część) i nie zdradza rozmiaru zbioru danych. Dodatkowo działa w środowisku rozproszonym, gdzie każdy węzeł może generować ID bez konieczności synchronizacji.
Czy wielkość liter i myślniki wpływają na wydajność przechowywania? W bezpośrednim przechowywaniu teksowym (np. VARCHAR(36)) wielkość liter nie zmienia rozmiaru – każdy znak to jeden bajt. Myślniki natomiast zajmują 4 dodatkowe bajty. W formacie binarnym (UUID jako 16 bajtów) ani wielkość liter, ani myślniki nie mają znaczenia.
Jakie formaty identyfikatorów można wygenerować na tej stronie? Strona oferuje wybór formatu, a domyślnym i opisanym w tej instrukcji jest UUID w wersji 7. Inne wersje (jak v4) mogą być dostępne w ramach tego samego narzędzia, ale nie są przedmiotem tego artykułu.
Czy generowanie działa offline? Po pierwszym załadowaniu strony kod JavaScript jest w pełni dostępny lokalnie. Nie wymaga połączenia internetowego do działania, o ile używasz silnego generatora liczb losowych wbudowanego w przeglądarkę. To samo dotyczy kopiowania wyników do schowka.