UUID v1 Generator
Generate time-based UUID v1 with embedded timestamp
…
Formátovat
Co je UUID v1?
UUID v1 je původní verzí UUID, standardizovanou v RFC 4122 (2005). Generuje jedinečné identifikátory kombinací vysoce přesného časového razítka s MAC adresou generujícího hostitele a krátkou sekvencí hodin pro zpracování rozlišení pod časovým razítkem.
Protože je časové razítko vloženo, hodnoty UUID v1 ze stejného hostitele monotónně rostou s časem — což je přirozeně seřazuje. To bylo navrženo pro distribuované systémy, kde každý uzel mohl generovat UUID nezávisle bez koordinace.
Dnes je UUID v1 z velké části nahrazeno UUID v7 (seřaditelné, bez úniku MAC) a UUID v4 (plně náhodné, soukromé). Stále se používá v systémech jako Apache Cassandra a starší distribuované databáze.
Anatomy of a UUID v1
Řetězec UUID v1 jako 550e8400-e29b-11d4-a716-446655440000 kóduje šest různých polí:
| Pole | Velikost | Popis |
|---|---|---|
| time_low | 32 bits | 32bitové nízké pole 60bitového gregoriánského časového razítka (intervaly 100 nanosekund od 15. října 1582) |
| time_mid | 16 bits | Střední 16bitové pole 60bitového časového razítka |
| time_hi_and_version | 16 bits | Horních 12 bitů 60bitového časového razítka plus 4bitové číslo verze (vždy <code>1</code>) |
| clock_seq_hi_res | 8 bits | 6bitové vysoké pole sekvence hodin kombinované se 2bitovou značkou varianty RFC 4122 |
| clock_seq_low | 8 bits | Nižší 8 bitů sekvence hodin |
| node | 48 bits | 48bitový identifikátor uzlu — typicky MAC adresa generujícího síťového rozhraní, nebo náhodná 48bitová hodnota, pokud není k dispozici žádná MAC |
Pole sekvence hodin (clock_seq_hi_res + clock_seq_low) je 14bitový čítač. Je inkrementován, kdykoliv se systémové hodiny pohybují zpět (např. NTP úprava) nebo když se systém restartuje bez zachování posledního známého časového razítka. To zabraňuje generování duplicitních UUID, pokud hodiny nejsou monotónně rostoucí.
Dekódování časového razítka UUID v1
60bitové časové razítko je rozptýleno napříč třemi poli v UUID. Pro rekonstrukci času generování:
- Extrahujte
time_low(bajty 0–3),time_mid(bajty 4–5) atime_hi(bajty 6–7, minus nibble verze) - Znovu sestavte:
(time_hi << 48) | (time_mid << 32) | time_low - Výsledkem je 60bitový počet
intervalů 100 nanosekundod 15. října 1582 (epocha gregoriánského kalendáře) - Odečtěte gregoriánsko-unix offset: 122 192 928 000 000 000 (intervaly 100 ns mezi 15. říj. 1582 a 1. led. 1970)
- Vydělte
10 000pro převod intervalů 100 nanosekund na milisekundy - Použijte výsledek jako
Unix milisekundové časové razítkopro vytvoření objektu Date - Formátujte jako
ISO 8601pro lidsky čitelný výstup
Přesnost časového razítka je 100 nanosekund — mnohem jemnější než milisekundová přesnost UUID v7. V praxi však většina operačních systémů nevystavuje rozlišení hodin pod milisekundy, takže nižší bity jsou často nula nebo syntetizované.
Obavy o soukromí
Nejvýznamnější nevýhodou UUID v1 je, že v poli uzlu obsahuje MAC adresu generujícího hostitele. To znamená, že každé UUID v1 nese trvalý, globálně jedinečný otisk prstu stroje, který ho vygeneroval.
Útočník, který získá UUID v1, může určit: (1) přibližný čas, kdy bylo ID vygenerováno, (2) MAC adresu generujícího hostitele, a (3) analýzou více UUID rychlost, s jakou jsou ID generována.
Z tohoto důvodu by UUID v1 nemělo být nikdy používáno jako veřejně přístupný identifikátor (např. v URL nebo odpovědích API), pokud nejste spokojeni se zveřejněním těchto informací. Samotný RFC 4122 poznamenává, že systém může použít náhodnou 48bitovou hodnotu místo MAC adresy, ale mnoho implementací to nedělá.
Kdy je UUID v1 stále vhodné
UUID v1 vs UUID v7
UUID v7 je moderní nástupce UUID v1 pro časově uspořádané identifikátory. Zde je přímé srovnání:
| Aspekt | UUID v1 | UUID v7 |
|---|---|---|
| Epocha / Časová základna | Gregoriánská epocha (15. říj. 1582) | Unix epocha (1. led. 1970) |
| Přesnost | 100 nanosekund | 1 milisekunda |
| Identifikátor uzlu | MAC adresa (prozrazuje identitu hostitele) | Náhodný (soukromý) |
| Soukromí | Prozrazuje MAC adresu a časové razítko generování | Žádné informace o hostiteli vloženy |
| Výkon DB indexu | Dobrý — sekvenční na hostitele | Výborný — k-seřaditelné přes všechny generátory |
| Standard | RFC 4122 (2005) | RFC 9562 (2024) |
Pro nové projekty je UUID v7 doporučenou náhradou za UUID v1. Poskytuje podobné záruky časového uspořádání bez soukromostních důsledků vkládání MAC adresy hostitele.
Příklady kódu
Generování UUID v1 není nativně dostupné v prohlížečích nebo Node.js. Použijte balíček uuid npm:
// 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))
}