Generator UUID v1

Generuje UUID v1 oparte na czasie z wbudowanym znacznikiem czasu

Formatuj

Liczba:
Note:UUID v1 zawiera adres MAC hosta i znacznik czasu generowania, co rodzi obawy dotyczące prywatności w większości nowoczesnych aplikacji. W przypadku nowych projektów zaleca się UUID v4, chyba że potrzebujesz konkretnie identyfikatorów posortowanych według czasu z możliwością dekodowania.

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:

PoleRozmiarOpis
time_low32 bits32-bitowe pole dolne 60-bitowego znacznika czasu gregoriańskiego (interwały 100-nanosekundowe od 15 października 1582)
time_mid16 bitsŚrodkowe 16-bitowe pole 60-bitowego znacznika czasu
time_hi_and_version16 bitsGórne 12 bitów 60-bitowego znacznika czasu plus 4-bitowy numer wersji (zawsze <code>1</code>)
clock_seq_hi_res8 bits6-bitowe pole wysokie sekwencji zegarowej połączone z 2-bitowym znacznikiem wariantu RFC 4122
clock_seq_low8 bitsDolne 8 bitów sekwencji zegarowej
node48 bits48-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:

  1. Wyodrębnij time_low (bajty 0–3), time_mid (bajty 4–5) i time_hi (bajty 6–7, minus nibble wersji)
  2. Złóż ponownie: (time_hi &lt;&lt; 48) | (time_mid &lt;&lt; 32) | time_low
  3. Wynik to 60-bitowa liczba interwałów 100-nanosekundowych od 15 października 1582 (epoka kalendarza gregoriańskiego)
  4. 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)
  5. Podziel przez 10 000, aby przekonwertować interwały 100-nanosekundowe na milisekundy
  6. Użyj wyniku jako uniksowego znacznika czasu w milisekundach, aby skonstruować obiekt Date
  7. Sformatuj jako ISO 8601 dla 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

Klucze główne Apache Cassandra
Cassandra używa UUID v1 (poprzez typ TimeUUID) jako podstawowego wzorca projektowego. Kolejność znaczników czasu naturalnie mapuje się na model przechowywania Cassandry, umożliwiając wydajne zapytania zakresowe według czasu.
Starsze systemy rozproszone
Systemy zbudowane przed istnieniem UUID v7 (przed 2024 r.), które polegają na UUID posortowanych według znacznika czasu i nie mogą łatwo migrować do nowego formatu.
Dzienniki audytu i zdarzeń
Gdy tożsamość generującego hosta jest znana i zaufana, osadzenie adresu MAC może zapewnić dodatkową identyfikowalność dla zdarzeń audytu.
Identyfikatory wewnętrzne
Identyfikatory, które nigdy nie są ujawniane poza kontrolowanym systemem wewnętrznym, gdzie ujawnianie adresu MAC nie jest problemem.
Zapytania zakresowe według czasu bez osobnej kolumny znacznika czasu
Osadzony znacznik czasu może być zdekodowany, aby filtrować wiersze według czasu generowania, działając jako połączony identyfikator i znacznik czasu.
Interoperacyjność ze starszymi generatorami UUID v1
Przy odbieraniu lub przetwarzaniu wartości UUID v1 z systemów zewnętrznych, które je produkują, dekodowanie osadzonego znacznika czasu do wyświetlania lub analizy.

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:

AspektUUID v1UUID v7
Epoka / Podstawa czasuEpoka gregoriańska (15 października 1582)Epoka uniksowa (1 stycznia 1970)
Precyzja100 nanosekund1 milisekunda
Identyfikator węzłaAdres MAC (ujawnia tożsamość hosta)Losowy (prywatny)
PrywatnośćUjawnia adres MAC i znacznik czasu generowaniaBrak osadzonych informacji o hoście
Wydajność indeksu bazy danychDobra — sekwencyjna na hostaDoskonała — k-sortowalna we wszystkich generatorach
StandardRFC 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:

JavaScript (browser)
// 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.789Z
Python
import 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"
Go
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))
}

Często zadawane pytania

Czy mogę zdekodować znacznik czasu z UUID v1?
Tak. Znacznik czasu generowania jest w pełni odtwarzalny z ciągu UUID v1. To narzędzie robi dokładnie to — wklej dowolny UUID v1, a wyświetli zdekodowany znacznik czasu UTC. Zapoznaj się z powyższymi krokami dekodowania dla algorytmu.
Czy adres MAC jest zawsze obecny w UUID v1?
Niekoniecznie. RFC 4122 pozwala implementacjom zastąpić losowo wygenerowaną 48-bitową wartością adres MAC, gdy interfejs sieciowy nie jest dostępny lub gdy pożądana jest prywatność. W praktyce wiele implementacji po stronie serwera osadza rzeczywisty adres MAC. Wartości UUID v1 generowane przez przeglądarki zawsze używają losowej wartości węzła, ponieważ przeglądarki nie udostępniają adresów MAC.
Dlaczego znacznik czasu UUID v1 używa 1582 jako epoki?
Reforma kalendarza gregoriańskiego weszła w życie 15 października 1582. Znacznik czasu UUID v1 został zdefiniowany względem tej daty, aby zapewnić stabilny, uniwersalny punkt odniesienia poprzedzający epokę uniksową (1970). Daje to 60-bitowemu polu znacznika czasu wystarczający zakres, aby pozostać unikalnym do około 3400 roku n.e.
UUID v1 a UUID v7 — kiedy nadal powinienem używać v1?
Głównym powodem używania UUID v1 dzisiaj jest zgodność z istniejącymi systemami — szczególnie Apache Cassandra, który używa v1 jako swojego typu TimeUUID. Dla wszystkich nowych systemów UUID v7 jest zdecydowanie lepszy: używa bardziej znajomej epoki uniksowej, nie ma wycieku adresu MAC i zapewnia lepszą wydajność indeksu B-tree.
Czy wartości UUID v1 mogą kolidować?
Teoretycznie dwie wartości UUID v1 mogą kolidować, jeśli ten sam adres MAC generuje dwa UUID w ciągu tego samego interwału 100-nanosekundowego, a sekwencja zegarowa jest identyczna. Sekwencja zegarowa istnieje właśnie po to, aby temu zapobiec — jest inkrementowana przy szybkich kolejnych wywołaniach. W praktyce kolizje UUID v1 są niezwykle rzadkie w poprawnie zaimplementowanych systemach.