Possibilità di generare Mongo ObjectId duplicati in due diverse raccolte?


187

È possibile generare lo stesso Mongo ObjectId esatto per un documento in due raccolte diverse? Mi rendo conto che è sicuramente molto improbabile, ma è possibile?

Senza essere troppo specifici, il motivo per cui lo chiedo è che con un'applicazione a cui sto lavorando mostriamo profili pubblici di funzionari eletti che speriamo di convertire in utenti a pieno titolo del nostro sito. Abbiamo raccolte separate per gli utenti e i funzionari eletti che non sono attualmente membri del nostro sito. Esistono vari altri documenti contenenti vari dati relativi ai funzionari eletti che tutti riconducono alla persona che utilizza il suo ObjectId ufficiale eletto.

Dopo aver creato l'account, evidenziamo ancora i dati associati al funzionario eletto ma ora fanno anche parte della raccolta di utenti con un ObjectId di utenti corrispondente per mappare il loro profilo alle interazioni con la nostra applicazione.

Avevamo iniziato a convertire la nostra applicazione da MySql a Mongo alcuni mesi fa e mentre siamo in fase di transizione archiviamo l'id MySql legacy per entrambi questi tipi di dati e stiamo anche iniziando a archiviare l'oggetto Mongo ufficiale eletto negli utenti documento da mappare ai dati ufficiali eletti.

Stavo meditando di specificare il nuovo User ObjectId come precedente ObjectId ufficiale eletto per semplificare le cose, ma volevo assicurarmi che non fosse possibile avere una collisione con nessun ObjectId utente esistente.

Grazie per la tua comprensione.

Modifica: poco dopo aver pubblicato questa domanda, mi sono reso conto che la mia soluzione proposta non era una buona idea. Sarebbe meglio solo mantenere lo schema attuale che abbiamo in atto e semplicemente collegarsi al "_id" ufficiale eletto nel documento degli utenti.



1
Ho letto quella pagina prima. Per ironia della sorte, in realtà ho collegato alla stessa pagina in una risposta precedente. E ho visto la dichiarazione di non responsabilità "ragionevolmente alta di essere unica", ma non ero sicuro che la raccolta che veniva inserita avesse avuto un ruolo in questo. Immagino che ciò di cui non sono sicuro sia ciò che rappresenta esattamente la porzione ID processo a 2 byte dell'ObjectId. Se ha qualcosa a che fare con la collezione, allora ci sarebbe unicità tra due diversi documenti creati contemporaneamente sulla stessa macchina in raccolte diverse.
Anthony Jack,

1
L'ID del processo a 2 byte è il pid del processo che genera l'ObjectID. Ad esempio, ecco il codice che pymongo utilizza per generare ObjectID: github.com/mongodb/mongo-python-driver/blob/master/bson/…
mstearn

Un gotcha in cui mi sono imbattuto è l'inserimento in batch. Stavo costruendo lotti di documenti da 10k e scontrandomi ogni volta perché la controparte si ribaltava ogni volta.
Fawce,

So che è passato un po 'di tempo, ma i documenti da 10K non sarebbero passati sul bancone. La parte contatore è di tre byte, non di tre cifre. Sono oltre 16 milioni.
Asya Kamsky,

Risposte:


318

Risposta breve

Solo per aggiungere una risposta diretta alla domanda iniziale: SÌ, se si utilizza la generazione di ID oggetto BSON, allora per la maggior parte dei driver gli ID saranno quasi sicuramente unici in tutte le raccolte. Vedi sotto per cosa significa "quasi certamente".

Risposta lunga

È molto probabile che l'ID oggetto BSON generato dai driver Mongo DB sia univoco tra le raccolte. Ciò è principalmente dovuto agli ultimi 3 byte dell'ID, che per la maggior parte dei driver viene generato tramite un contatore incrementale statico. Quel contatore è indipendente dalla raccolta; è globale. Il driver Java, ad esempio, utilizza un AtomicInteger statico casualmente inizializzato.

Quindi perché, nei documenti Mongo, dicono che gli ID sono "altamente probabili" per essere unici, invece di dire apertamente che saranno unici? Possono verificarsi tre possibilità in cui non si ottiene un ID univoco (per favore fatemi sapere se ci sono più):

Prima di questa discussione, ricorda che l'ID oggetto BSON è costituito da:

[4 byte secondi dall'epoca, hash macchina 3 byte, ID processo 2 byte, contatore 3 byte]

Ecco le tre possibilità, quindi giudichi tu stesso quanto è probabile ottenere un duplicato:

1) Overflow del contatore: ci sono 3 byte nel contatore. Se ti capita di inserire più di 16.777.216 (2 ^ 24) documenti in un solo secondo, sullo stesso computer, nello stesso processo, puoi overflow dei byte del contatore incrementale e finire con due ID oggetto che condividono lo stesso tempo, computer , processo e valori contatore.

2) Contatore non incrementale: alcuni driver Mongo usano numeri casuali invece di incrementare i numeri per i byte contatore. In questi casi, esiste una possibilità 1 / 16.777.216 di generare un ID non univoco, ma solo se questi due ID vengono generati nello stesso secondo (ovvero prima che la sezione temporale dell'ID si aggiorni al secondo successivo), sullo stesso macchina, nello stesso processo.

3) Hash della macchina e del processo con gli stessi valori. I valori di ID macchina e ID processo possono, in alcuni scenari altamente improbabili, essere associati agli stessi valori per due macchine diverse. In questo caso, e allo stesso tempo i due contatori sulle due macchine diverse, durante lo stesso secondo, generano lo stesso valore, quindi ti ritroverai con un ID duplicato.

Questi sono i tre scenari a cui prestare attenzione. Gli scenari 1 e 3 sembrano altamente improbabili e lo scenario 2 è totalmente evitabile se si utilizza il driver giusto. Dovrai controllare la fonte del driver per sapere con certezza.


Il contatore a 3 byte non rappresenta una capacità di accettare 2 ^ 24 = 16777216 numero di documenti inseriti al secondo per processo per macchina?
Forrest,

Hai perfettamente ragione, ho dimezzato per errore il numero di bit - la risposta è stata modificata.
Raj Advani,

Da quando sono appena entrato in questo, vorrei aggiungere che alcuni piloti (ad es. C), sebbene utilizzi gli incrementi, non aumentano atomicamente, quindi di volta in volta generano lo stesso oid a causa delle condizioni di gara
Pawel Veselov

39
Hai completamente ignorato il fatto che in 136 anni avresti avuto un altro colpo per generare lo stesso ObjectIdche avevi prima fintanto che l'hash della macchina, l'ID di processo e il contatore risultano tutti uguali
jamylak,

25
@jamylak Ci occuperemo di quel problema quando diventerà urgente (hanno detto quelle persone che hanno standardizzato i formati di data YYMMDD negli anni '70)
Philipp

14

Gli ObjectId vengono generati sul lato client in un modo simile a UUID ma con alcune proprietà più piacevoli per l'archiviazione in un database come aumentare all'incirca l'ordine e codificare il tempo di creazione gratuitamente. La cosa fondamentale per il tuo caso d'uso è che sono progettati per garantire unicità ad un'alta probabilità anche se sono generati su macchine diverse.

Ora, se ci si riferiva al campo _id in generale, non è richiesta l'unicità tra le raccolte, quindi è possibile riutilizzare in sicurezza il vecchio _id. Ad esempio concreto, se hai due raccolte, colorsed fruitsentrambe potrebbero avere contemporaneamente un oggetto simile {_id: 'orange'}.

Se vuoi saperne di più su come vengono creati gli ObjectId, ecco le specifiche: http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification


11

Nel caso in cui qualcuno abbia problemi con duplicati di Mongo ObjectID, dovresti sapere che nonostante l'improbabilità che i dups avvengano in Mongo stesso, è possibile avere _id duplicati generati con PHP in Mongo.

Il caso d'uso in cui ciò è accaduto con regolarità per me è quando eseguo il ciclo continuo di un set di dati e tento di iniettare i dati in una raccolta.

L'array che contiene i dati di iniezione deve essere reimpostato esplicitamente su ogni iterazione, anche se non si specifica il valore _id. Per qualche motivo, il processo INSERT aggiunge Mongo _id all'array come se fosse una variabile globale (anche se l'array non ha un ambito globale). Ciò può influire anche se si sta chiamando l'inserimento in una chiamata di funzione separata dove normalmente ci si aspetterebbe che i valori dell'array non persistano nella funzione chiamante.

Esistono tre soluzioni a questo:

  1. È possibile unset()il campo _id dall'array
  2. È possibile reinizializzare l'intero array array()ogni volta che si esegue il ciclo del set di dati
  3. Puoi definire tu stesso esplicitamente il valore _id (avendo cura di definirlo in modo tale da non generare tu stesso duplicati).

La mia ipotesi è che questo sia un bug nell'interfaccia di PHP, e non tanto un problema con Mongo, ma se ti imbatti in questo problema, disattiva il _id e dovresti andare bene.


vedi qui: php.net/manual/en/mongocollection.insert.php : "Nota: se il parametro non ha una chiave o proprietà _id, verrà creata e assegnata una nuova istanza MongoId. Questo comportamento speciale non significa che il parametro viene passato per riferimento. ", è una funzionalità, non un bug, è pensato per essere così
Oliver Konig

1
Non capisco lo scenario che stai descrivendo qui; forse potresti mostrare del codice che mostra il bug?
Mark Amery,

-7

Non esiste alcuna garanzia sull'unicità di ObjectId tra le raccolte. Anche se è probabilisticamente molto improbabile, si tratterebbe di un design di applicazione molto scadente che si basava sull'unicità di _id tra le raccolte.

Si può facilmente provare questo nella shell mongo:

MongoDB shell version: 1.6.5
connecting to: test
> db.foo.insert({_id: 'abc'})
> db.bar.insert({_id: 'abc'})
> db.foo.find({_id: 'abc'})
{ "_id" : "abc" }
> db.bar.find({_id: 'abc'})
{ "_id" : "abc" }
> db.foo.insert({_id: 'abc', data:'xyz'})
E11000 duplicate key error index: test.foo.$_id_  dup key: { : "abc" }

Quindi, non fare assolutamente affidamento sul fatto che _id sia unico in tutte le raccolte e poiché non si controlla la funzione di generazione di ObjectId, non fare affidamento su di essa.

È possibile creare qualcosa che assomigli di più a un uuido e, se lo fai manualmente, potresti avere una migliore garanzia di unicità.

Ricorda che puoi inserire oggetti di "tipi" diversi nella stessa raccolta, quindi perché non mettere semplicemente i tuoi due "tavoli" nella stessa raccolta. Condividono lo stesso spazio _id, e quindi, sarebbero garantiti unici. Passare da "potenziale" a "registrato" sarebbe un semplice capovolgimento di un campo ...


1
Penso che potresti confondere il campo _id in generale con il tipo ObjectID. Il tipo ObjectID è stato progettato specificamente per l'unicità con l'obiettivo di poter essere trattato come un UUID. Tuttavia, il campo _id può essere di qualsiasi tipo e garantisce l'univocità su una singola raccolta se si utilizzano altri tipi per la chiave, come una stringa nell'esempio.
mstearn,

@mstearn (Nitpick) L'idea che un UUID sia intrinsecamente unico è errata. Una buona strategia di generazione di UUID / sequenza può rendere improbabile la collisione, ma deve tenere conto di generatori unici (ad es. Posizioni uniche) per garantire l' assoluta unicità tra i generatori. Certo, la maggior parte ha probabilità così basse che non sono di interesse :-) GUID . Una questione che fa venire, però, è la duplicazione / copia di ID invece di una nuova generazione.

1
@pst: gli ObjectID MongoDB includono sia il pid del processo di generazione che alcuni byte basati su un hash del nome host. Questi combinati con un timestamp e un contatore incrementale rendono estremamente probabile che due ObjectID generati separatamente siano univoci a livello globale / universale. Ovviamente, come hai detto, si applica solo agli ObjectID appena generati.
mstearn,

1
Mi riferisco al tipo ObjectId. Non specificando un valore di stringa per '_id'. Naturalmente saranno gli stessi e saranno in conflitto se li imposti manualmente sulla stessa stringa esatta.
Anthony Jack,

Sì, ho chiarito le cose nel mio post. I _id non sono certamente unici, e poiché non controlli la funzione di generazione di ObjectId, è probabilmente una cattiva idea fare affidamento su di esso.
slacy
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.