Generator UUID v1
Generuje UUID v1 oparte na czasie z wbudowanym znacznikiem czasu
…
Formatuj
Czym jest UUID v1?
UUID v1 to oryginalna wersja UUID, standaryzowana w RFC 4122 (2005). Generuje unikalne identyfikatory poprzez połączenie znacznika czasu wysokiej precyzji z adresem MAC generującego hosta oraz krótką sekwencją zegarową obsługującą rozdzielczość poniżej znacznika czasu.
Ponieważ znacznik czasu jest osadzony, wartości UUID v1 z tego samego hosta monotonicznie rosną w czasie — co sprawia, że są naturalnie uporządkowane. Zostały zaprojektowane dla systemów rozproszonych, w których każdy węzeł mógł niezależnie generować UUID bez koordynacji.
Dziś UUID v1 jest w dużej mierze zastąpiony przez UUID v7 (sortowalny, bez wycieku MAC) i UUID v4 (w pełni losowy, prywatny). Nadal jest używany w systemach takich jak Apache Cassandra i starszych rozproszonych bazach danych.
Anatomia UUID v1
Ciąg UUID v1 taki jak 550e8400-e29b-11d4-a716-446655440000 koduje sześć odrębnych pól:
| Pole | Rozmiar | Opis |
|---|---|---|
| time_low | 32 bits | 32-bitowe pole dolne 60-bitowego znacznika czasu gregoriańskiego (interwały 100-nanosekundowe od 15 października 1582) |
| time_mid | 16 bits | Środkowe 16-bitowe pole 60-bitowego znacznika czasu |
| time_hi_and_version | 16 bits | Górne 12 bitów 60-bitowego znacznika czasu plus 4-bitowy numer wersji (zawsze <code>1</code>) |
| clock_seq_hi_res | 8 bits | 6-bitowe pole wysokie sekwencji zegarowej połączone z 2-bitowym znacznikiem wariantu RFC 4122 |
| clock_seq_low | 8 bits | Dolne 8 bitów sekwencji zegarowej |
| node | 48 bits | 48-bitowy identyfikator węzła — zazwyczaj adres MAC generującego interfejsu sieciowego lub losowa 48-bitowa wartość, jeśli MAC nie jest dostępny |
Pole sekwencji zegarowej (clock_seq_hi_res + clock_seq_low) to 14-bitowy licznik. Jest inkrementowany zawsze, gdy zegar systemowy cofa się (np. korekta NTP) lub gdy system restartuje się bez zachowania ostatniego znanego znacznika czasu. Zapobiega to generowaniu zduplikowanych UUID, jeśli zegar nie jest monotonicznie rosnący.
Dekodowanie znacznika czasu UUID v1
60-bitowy znacznik czasu jest rozproszony po trzech polach UUID. Aby zrekonstruować czas generowania:
- Wyodrębnij
time_low(bajty 0–3),time_mid(bajty 4–5) itime_hi(bajty 6–7, minus nibble wersji) - Złóż ponownie:
(time_hi << 48) | (time_mid << 32) | time_low - Wynik to 60-bitowa liczba
interwałów 100-nanosekundowychod 15 października 1582 (epoka kalendarza gregoriańskiego) - Odejmij przesunięcie gregoriańsko-uniksowe: 122 192 928 000 000 000 (interwały 100-ns między 15 października 1582 a 1 stycznia 1970)
- Podziel przez
10 000, aby przekonwertować interwały 100-nanosekundowe na milisekundy - Użyj wyniku jako
uniksowego znacznika czasu w milisekundach, aby skonstruować obiekt Date - Sformatuj jako
ISO 8601dla czytelnego dla człowieka wyjścia
Precyzja znacznika czasu wynosi 100 nanosekund — znacznie drobniejsza niż milisekundowa precyzja UUID v7. Jednak w praktyce większość systemów operacyjnych nie udostępnia rozdzielczości zegara poniżej milisekund, więc dolne bity są często zerowe lub syntetyzowane.
Obawy dotyczące prywatności
Najbardziej znaczącą wadą UUID v1 jest to, że osadza adres MAC generującego hosta w polu węzła. Oznacza to, że każdy UUID v1 nosi trwały, globalnie unikalny odcisk palca maszyny, która go wygenerowała.
Przeciwnik, który uzyska UUID v1, może określić: (1) przybliżony czas wygenerowania identyfikatora, (2) adres MAC generującego hosta, i (3) analizując wiele UUID, tempo generowania identyfikatorów.
Z tego powodu UUID v1 nigdy nie powinien być używany jako identyfikator publiczny (np. w URL-ach lub odpowiedziach API), chyba że jest się komfortowym z ujawnianiem tych informacji. Sam RFC 4122 zauważa, że system może użyć losowej 48-bitowej wartości zamiast adresu MAC, ale wiele implementacji tego nie robi.
Kiedy UUID v1 jest nadal odpowiedni
UUID v1 a UUID v7
UUID v7 jest nowoczesnym następcą UUID v1 dla identyfikatorów posortowanych według czasu. Oto bezpośrednie porównanie:
| Aspekt | UUID v1 | UUID v7 |
|---|---|---|
| Epoka / Podstawa czasu | Epoka gregoriańska (15 października 1582) | Epoka uniksowa (1 stycznia 1970) |
| Precyzja | 100 nanosekund | 1 milisekunda |
| Identyfikator węzła | Adres MAC (ujawnia tożsamość hosta) | Losowy (prywatny) |
| Prywatność | Ujawnia adres MAC i znacznik czasu generowania | Brak osadzonych informacji o hoście |
| Wydajność indeksu bazy danych | Dobra — sekwencyjna na hosta | Doskonała — k-sortowalna we wszystkich generatorach |
| Standard | RFC 4122 (2005) | RFC 9562 (2024) |
W przypadku nowych projektów UUID v7 jest zalecanym zastępcą UUID v1. Zapewnia podobne gwarancje kolejności czasowej bez konsekwencji dla prywatności związanych z osadzaniem adresu MAC hosta.
Przykłady kodu
Generowanie UUID v1 nie jest dostępne natywnie w przeglądarkach ani Node.js. Użyj pakietu npm uuid:
// Generate a UUID v1 using the Web Crypto API
function generateUuidV1() {
const buf = new Uint8Array(16)
crypto.getRandomValues(buf)
const ms = BigInt(Date.now())
const gregorianOffset = 122192928000000000n
const t = ms * 10000n + gregorianOffset
const tLow = Number(t & 0xFFFFFFFFn)
const tMid = Number((t >> 32n) & 0xFFFFn)
const tHiVer = Number((t >> 48n) & 0x0FFFn) | 0x1000 // version 1
const clockSeq = (buf[8] & 0x3F) | 0x80 // variant 10xxxxxx
const clockSeqLow = buf[9]
const hex = (n, pad) => n.toString(16).padStart(pad, '0')
const node = [...buf.slice(10)].map(b => b.toString(16).padStart(2, '0')).join('')
return `${hex(tLow,8)}-${hex(tMid,4)}-${hex(tHiVer,4)}-${hex(clockSeq,2)}${hex(clockSeqLow,2)}-${node}`
}
// Extract the embedded timestamp from a UUID v1
function extractTimestamp(uuid) {
const parts = uuid.split('-')
const tHex = parts[2].slice(1) + parts[1] + parts[0]
const t = BigInt('0x' + tHex)
const ms = (t - 122192928000000000n) / 10000n
return new Date(Number(ms))
}
const id = generateUuidV1()
console.log(id) // e.g. "1eb5e8b0-6b4d-11ee-9c45-a1f2b3c4d5e6"
console.log(extractTimestamp(id)) // e.g. 2023-10-15T12:34:56.789Zimport uuid from datetime import datetime, timezone # Generate UUID v1 (uses MAC address by default) uid = uuid.uuid1() print(uid) # Extract embedded timestamp # uuid.time is 100-ns intervals since Oct 15, 1582 GREGORIAN_OFFSET = 122192928000000000 # 100-ns intervals ts_100ns = uid.time ts_ms = (ts_100ns - GREGORIAN_OFFSET) // 10000 dt = datetime.fromtimestamp(ts_ms / 1000, tz=timezone.utc) print(dt.isoformat()) # e.g. "2023-10-15T12:34:56.789000+00:00"
package main
import (
"fmt"
"time"
"github.com/google/uuid" // go get github.com/google/uuid
)
func main() {
id, _ := uuid.NewUUID() // UUID v1
fmt.Println(id)
// Extract timestamp from UUID v1
// uuid.Time is 100-ns ticks since Oct 15, 1582
t := id.Time()
sec := int64(t)/1e7 - 12219292800 // convert to Unix seconds
nsec := (int64(t) % 1e7) * 100
ts := time.Unix(sec, nsec).UTC()
fmt.Println(ts.Format(time.RFC3339Nano))
}