Quando dovrei usare uuid.uuid1 () vs. uuid.uuid4 () in Python?


207

Comprendo le differenze tra i due dai documenti.

uuid1():
Genera un UUID da un ID host, un numero di sequenza e l'ora corrente

uuid4():
Genera un UUID casuale.

Quindi uuid1utilizza le informazioni macchina / sequenza / tempo per generare un UUID. Quali sono i pro e i contro dell'utilizzo di ciascuno?

So che uuid1()può avere problemi di privacy, dal momento che si basa su informazioni sulla macchina. Mi chiedo se c'è qualcosa di più sottile nella scelta dell'uno o dell'altro. Uso solo uuid4()ora, dal momento che è un UUID completamente casuale. Ma mi chiedo se dovrei usare uuid1per ridurre il rischio di collisioni.

Fondamentalmente, sto cercando i consigli delle persone per le migliori pratiche sull'uso dell'uno contro l'altro. Grazie!


3
Ecco un approccio alternativo a UUID. Sebbene la possibilità di collisione sia UUID infinitesimale non garantisce l'unicità. Per garantire l'univocità, potresti voler usare la chiave composta come [<ID sistema>, <ID locale>]. Ogni sistema che partecipa alla condivisione dei dati deve avere il proprio ID univoco del sistema assegnato durante la configurazione del sistema o ottenuto da un pool comune di ID. L'ID locale è un ID univoco all'interno di un determinato sistema. Ciò comporta più problemi ma garantisce unicità. Ci scusiamo per l'offtopico, sto solo cercando di aiutare.
oᴉɹǝɥɔ

3
Non si occupa delle "preoccupazioni sulla privacy" che ha citato
Shrey,

Risposte:


253

uuid1()è garantito che non si producono collisioni (partendo dal presupposto che non ne crei troppe contemporaneamente). Non lo userei se è importante che non ci sia alcuna connessione tra uuidil computer e, poiché l'indirizzo mac viene utilizzato per renderlo unico su tutti i computer.

È possibile creare duplicati creando più di 2 14 uuid1 in meno di 100 ns, ma questo non è un problema per la maggior parte dei casi d'uso.

uuid4()genera, come hai detto, un UUID casuale. La possibilità di una collisione è davvero molto, molto piccola. Abbastanza piccolo, che non dovresti preoccupartene. Il problema è che un cattivo generatore di numeri casuali aumenta le probabilità di avere collisioni.

Questa eccellente risposta di Bob Aman lo riassume bene. (Consiglio di leggere l'intera risposta.)

Francamente, in un unico spazio di applicazione senza attori malvagi, l'estinzione di tutta la vita sulla Terra avverrà molto prima che tu abbia una collisione, anche su un UUID versione 4, anche se stai generando un bel po 'di UUID al secondo.


Siamo spiacenti, ho commentato senza effettuare ricerche complete: ci sono alcuni bit riservati per impedire che una versione 4 dell'UUID si scontri con una versione 1 dell'UUID. Rimuoverò il mio commento originale. Vedi tools.ietf.org/html/rfc4122
Mark Ransom il

1
@gs Sì, ha senso con quello che stavo leggendo. uuid1 è "più unico", mentre uuid4 è più anonimo. Quindi sostanzialmente usa uuid1 a meno che tu non abbia un motivo per non farlo. @mark ransom: risposta eccezionale, non ho trovato quando ho cercato uuid1 / uuid4. Sembra proprio dalla bocca del cavallo.
rocketmonkeys,

6
uuid1non produrrà necessariamente UUID univoci se ne produci diversi al secondo sullo stesso nodo. Esempio: [uuid.uuid1() for i in range(2)]. A meno che ovviamente non stia succedendo qualcosa di strano che mi manca.
Michael Mior,

1
@Michael: uuid1ha un numero progressivo (4 ° elemento nel tuo esempio), quindi se non usi tutti i bit nel contatore non hai alcuna collisione.
Georg Schölly,

3
@Michael: ho provato a ricercare le circostanze in cui si verificano le collisioni e ho aggiunto le informazioni che ho trovato.
Georg Schölly,

32

Un esempio quando si può prendere in considerazione uuid1(), piuttosto che uuid4()è quando UUID vengono prodotti su macchine separate , ad esempio quando più transazioni online sono processo su diverse macchine per scopi ridimensionamento.

In una situazione del genere, ad esempio, i rischi di collisioni dovuti a scelte errate nel modo in cui vengono inizializzati i generatori di numeri pseudo-casuali e anche il numero potenzialmente più elevato di UUID prodotti rende più probabile la possibilità di creare ID duplicati.

Un altro interesse di uuid1(), in quel caso, è che la macchina su cui inizialmente è stato prodotto ciascun GUID è implicitamente registrata (nella parte "nodo" di UUID). Questa e le informazioni sul tempo possono essere utili se non altro per il debug.


20

Il mio team ha appena avuto problemi con UUID1 per uno script di aggiornamento del database in cui abbiamo generato circa 120k UUID in un paio di minuti. La collisione UUID ha portato alla violazione di un vincolo chiave primaria.

Abbiamo aggiornato centinaia di server ma nelle nostre istanze Amazon EC2 abbiamo riscontrato questo problema alcune volte. Sospetto che la risoluzione dell'orologio sia scadente e il passaggio a UUID4 l'ha risolto per noi.


5

Una cosa da notare quando si utilizza uuid1, se si utilizza la chiamata predefinita (senza fornire il clock_seqparametro) si ha la possibilità di incorrere in collisioni: si ha solo 14 bit di casualità (la generazione di 18 voci in 100 ns offre all'incirca l'1% di probabilità di una collisione. paradosso / attacco di compleanno). Il problema non si verificherà mai nella maggior parte dei casi d'uso, ma su una macchina virtuale con una bassa risoluzione dell'orologio ti morde.


7
@Guilaume sarebbe davvero utile vedere un esempio di buona pratica usando clock_seq....
eric,

@Guilaume Come hai calcolato questa possibilità dell'1%? 14 bit di casualità indicano che si verificherà la collisione se si generano> = 2 ^ 14 ID per 100 ns e ciò significa che l'1% di probabilità di una collisione è quando si producono circa 163 ID per 100 ns
maks

1
@maks Come ho detto, dovresti guardare al paradosso del compleanno .
Guillaume,

3

Forse qualcosa che non è stato menzionato è quello della località.

Un indirizzo MAC o un ordinamento basato sul tempo (UUID1) può offrire maggiori prestazioni del database, poiché è meno lavoro per ordinare i numeri più vicini di quelli distribuiti in modo casuale (UUID4) (vedere qui ).

Un secondo problema correlato è che l'utilizzo di UUID1 può essere utile nel debug, anche se i dati di origine vengono persi o non vengono archiviati in modo esplicito (questo è ovviamente in conflitto con il problema di privacy menzionato dall'OP).


1

Oltre alla risposta accettata, esiste una terza opzione che può essere utile in alcuni casi:

v1 con MAC casuale ("v1mc")

È possibile creare un ibrido tra v1 e v4 generando deliberatamente UUID v1 con un indirizzo MAC di trasmissione casuale (ciò è consentito dalle specifiche v1). L'UUID v1 risultante dipende dal tempo (come il normale v1), ma manca di tutte le informazioni specifiche dell'host (come v4). È anche molto più vicino a v4 nella sua resistenza alle collisioni: v1mc = 60 bit di tempo + 61 bit casuali = 121 bit unici; v4 = 122 bit casuali.

Il primo posto che ho riscontrato è stata la funzione uuid_generate_v1mc () di Postgres . Da allora ho usato il seguente equivalente di Python:

from os import urandom
from uuid import uuid1
_int_from_bytes = int.from_bytes  # py3 only

def uuid1mc():
    # NOTE: The constant here is required by the UUIDv1 spec...
    return uuid1(_int_from_bytes(urandom(6), "big") | 0x010000000000)

(nota: ho una versione più lunga + più veloce che crea direttamente l'oggetto UUID; può pubblicare se qualcuno lo desidera)


In caso di GRANDI volumi di chiamate / secondo, ciò può esaurire la casualità del sistema. Si potrebbe utilizzare lo stdlib randommodulo invece (sarà probabilmente anche più veloce). Ma ATTENZIONE: bastano poche centinaia di UUID prima che un attaccante possa determinare lo stato di RNG e quindi prevedere parzialmente gli UUID futuri.

import random
from uuid import uuid1

def uuid1mc_insecure():
    return uuid1(random.getrandbits(48) | 0x010000000000)

Sembra che questo metodo sia "come" v4 (host-agnostico), ma peggio (meno bit, dipendenza da urandom, ecc.). Ci sono dei vantaggi rispetto a uuid4?
rocketmonkeys

Questo è principalmente solo un aggiornamento per i casi in cui v1 è utile per le sue qualità basate sul tempo, ma si desidera una maggiore resistenza alle collisioni e la privacy dell'host. Un esempio è come chiave primaria per un database: rispetto alla v4, gli uuidi v1 avranno una migliore localizzazione durante la scrittura su disco, un ordinamento naturale più utile, ecc. Ma se hai un caso in cui un utente malintenzionato prevede 2 ** 61 bit è un problema di sicurezza (ad es. Come uuid a nonce), quindi $ diety sì, usa invece uuid4 (so di sì!). Ri: peggio perché usa urandom, non sono sicuro di cosa intendi - sotto python, anche uuid4 () usa urandom.
Eli Collins,

Roba buona, ha senso. È bello vedere non solo cosa puoi fare (il tuo codice), ma anche perché lo vorresti. Ri: urandom, intendo che stai consumando il doppio della casualità (1 per uuid1, un altro per l'urandom), quindi potresti utilizzare l'entropia del sistema più velocemente.
rocketmonkeys

In realtà è circa la metà di uuid4: uuid1 () usa 14 bit per clock_seq, che arrotonda fino a 2 byte di urandom. Il wrapper uuid1mc utilizza 48 bit, che dovrebbero essere mappati a 6 byte di urandom, per un totale di urandom (8) consumati per chiamata. mentre uuid4 invoca direttamente urandom (16) per ogni chiamata.
Eli Collins,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.