Möglichkeit, dass doppelte Mongo ObjectIds in zwei verschiedenen Sammlungen generiert werden?

187

Ist es möglich, dass dieselbe exakte Mongo ObjectId für ein Dokument in zwei verschiedenen Sammlungen generiert wird? Mir ist klar, dass es definitiv sehr unwahrscheinlich ist, aber ist es möglich?

Der Grund, den ich frage, ist, dass wir mit einer Anwendung, an der ich arbeite, öffentliche Profile von gewählten Beamten anzeigen, die wir hoffentlich in vollwertige Benutzer unserer Website umwandeln können. Wir haben separate Sammlungen für Benutzer und gewählte Beamte, die derzeit keine Mitglieder unserer Website sind. Es gibt verschiedene andere Dokumente, die verschiedene Daten über die gewählten Beamten enthalten, die alle der Person mit ihrer gewählten offiziellen Objekt-ID zugeordnet sind.

Nach dem Erstellen des Kontos markieren wir weiterhin die Daten, die dem gewählten Beamten zugeordnet sind, aber sie sind jetzt auch Teil der Benutzersammlung mit einer entsprechenden Benutzer-ObjectId, um ihr Profil Interaktionen mit unserer Anwendung zuzuordnen.

Wir haben vor einigen Monaten damit begonnen, unsere Anwendung von MySql auf Mongo zu konvertieren. Während des Übergangs speichern wir die alte MySql-ID für beide Datentypen und beginnen nun auch damit, die gewählte offizielle Mongo ObjectId in den Benutzern zu speichern Dokument, um es wieder den gewählten offiziellen Daten zuzuordnen.

Ich habe darüber nachgedacht, nur die neue Benutzer-Objekt-ID als die zuvor gewählte offizielle Objekt-ID anzugeben, um die Dinge zu vereinfachen, wollte aber sicherstellen, dass es nicht möglich ist, eine Kollision mit einer vorhandenen Benutzer-Objekt-ID zu haben.

Vielen Dank für Ihren Einblick.

Bearbeiten: Kurz nachdem ich diese Frage gestellt hatte, stellte ich fest, dass meine vorgeschlagene Lösung keine sehr gute Idee war. Es wäre besser, nur das aktuelle Schema beizubehalten und nur auf die gewählte offizielle '_id' im Benutzerdokument zu verlinken.

Anthony Jack
quelle
1
Ich habe diese Seite schon einmal gelesen. Ironischerweise habe ich in einer früheren Antwort tatsächlich auf dieselbe Seite verlinkt. Und ich habe den Haftungsausschluss "einigermaßen hohe Wahrscheinlichkeit, einzigartig zu sein" gesehen, war mir aber nicht sicher, ob die eingefügte Sammlung einen Einfluss darauf hat. Ich bin mir nicht sicher, was genau der 2-Byte-Prozess-ID-Teil der ObjectId wirklich darstellt. Wenn es etwas mit der Sammlung zu tun hat, besteht eine Eindeutigkeit zwischen zwei verschiedenen Dokumenten, die genau zur gleichen Zeit auf genau demselben Computer in verschiedenen Sammlungen erstellt wurden.
Anthony Jack
1
Die 2-Byte-Prozess-ID ist die PID des Prozesses, der die ObjectID generiert. Als Beispiel ist hier der Code, den Pymongo verwendet, um ObjectIDs zu generieren: github.com/mongodb/mongo-python-driver/blob/master/bson/…
mstearn
Ein Problem, auf das ich gestoßen bin, ist das Batch-Einfügen. Ich habe Stapel von 10.000 Dokumenten erstellt und bin jedes Mal zusammengestoßen, weil der Zählerteil jedes Mal überrollt wurde.
Fawce
Ich weiß, dass es eine Weile her ist, aber 10.000 Dokumente würden nicht über den Ladentisch rollen. Das Gegenstück besteht aus drei Bytes, nicht aus drei Ziffern. Das sind über 16 Millionen.
Asya Kamsky

Antworten:

318

Kurze Antwort

Nur um eine direkte Antwort auf Ihre erste Frage hinzuzufügen: JA, wenn Sie die BSON-Objekt-ID-Generierung verwenden, werden die IDs für die meisten Treiber mit ziemlicher Sicherheit in allen Sammlungen eindeutig sein. Weiter unten erfahren Sie, was "mit ziemlicher Sicherheit" bedeutet.

Lange Antwort

Die von Mongo DB-Treibern generierten BSON-Objekt-IDs sind höchstwahrscheinlich in allen Sammlungen eindeutig. Dies liegt hauptsächlich an den letzten 3 Bytes der ID, die für die meisten Treiber über einen statischen Inkrementierungszähler generiert werden. Dieser Zähler ist sammlungsunabhängig; es ist global. Der Java-Treiber verwendet beispielsweise eine zufällig initialisierte statische AtomicInteger.

Warum sagen sie in den Mongo-Dokumenten, dass die IDs "höchstwahrscheinlich" eindeutig sind, anstatt direkt zu sagen, dass sie eindeutig sein werden? Es gibt drei Möglichkeiten, bei denen Sie keine eindeutige ID erhalten (lassen Sie mich wissen, wenn weitere vorhanden sind):

Denken Sie vor dieser Diskussion daran, dass die BSON-Objekt-ID besteht aus:

[4 Bytes Sekunden seit der Epoche, 3 Bytes Maschinen-Hash, 2 Bytes Prozess-ID, 3 Bytes Zähler]

Hier sind die drei Möglichkeiten, damit Sie selbst beurteilen können, wie wahrscheinlich es ist, dass Sie betrogen werden:

1) Zählerüberlauf: Der Zähler enthält 3 Bytes. Wenn Sie zufällig in einer Sekunde mehr als 16.777.216 (2 ^ 24) Dokumente auf demselben Computer im selben Prozess einfügen, können Sie die inkrementierenden Zählerbytes überlaufen und am Ende zwei Objekt-IDs erhalten, die dieselbe Zeit haben, Computer , Prozess- und Zählerwerte.

2) Zähler nicht inkrementierend: Einige Mongo-Treiber verwenden Zufallszahlen anstelle von inkrementierenden Zahlen für die Zählerbytes. In diesen Fällen besteht eine 1 / 16,777,216-Chance, eine nicht eindeutige ID zu generieren, jedoch nur, wenn diese beiden IDs in derselben Sekunde (dh bevor der Zeitabschnitt der ID auf die nächste Sekunde aktualisiert wird) auf derselben generiert werden Maschine, im gleichen Prozess.

3) Maschinen- und Prozess-Hash auf die gleichen Werte. Die Werte für Maschinen-ID und Prozess-ID können in einem höchst unwahrscheinlichen Szenario denselben Werten für zwei verschiedene Maschinen zugeordnet werden. Wenn dies auftritt und gleichzeitig die beiden Zähler auf den beiden verschiedenen Computern in derselben Sekunde denselben Wert generieren, erhalten Sie eine doppelte ID.

Dies sind die drei Szenarien, auf die Sie achten müssen. Szenario 1 und 3 scheinen höchst unwahrscheinlich, und Szenario 2 ist völlig vermeidbar, wenn Sie den richtigen Treiber verwenden. Sie müssen die Quelle des Treibers überprüfen, um sicher zu sein.

Raj Advani
quelle
Stellt der 3-Byte-Zähler nicht eine Fähigkeit dar, 2 ^ 24 = 16777216 Anzahl von Dokumenten zu akzeptieren, die pro Sekunde pro Prozess pro Maschine eingefügt wurden?
Forrest Ye
Sie haben absolut Recht, ich habe versehentlich die Anzahl der Bits halbiert - die Antwort wurde geändert.
Raj Advani
Da ich gerade erst darauf eingegangen bin, möchte ich hinzufügen, dass einige Fahrer (z. B. C), obwohl sie Inkremente verwenden, nicht atomar inkrementieren, so dass sie von Zeit zu Zeit aufgrund der Rennbedingungen dieselbe OID erzeugen
Pawel Veselov,
39
Sie haben die Tatsache völlig übersprungen, dass Sie in 136 Jahren einen weiteren Schuss haben würden, um das gleiche zu generieren, das ObjectIdSie zuvor hatten, solange der Maschinen-Hash, die Prozess-ID und der Zähler alle gleich
ausfallen
25
@ Jamylak Wir werden uns um dieses Problem kümmern, wenn es dringend wird (sagten diejenigen, die YYMMDD-Datumsformate in den 70er Jahren standardisiert haben)
Philipp
14

ObjectIds werden clientseitig auf ähnliche Weise wie UUID generiert, jedoch mit einigen besseren Eigenschaften für die Speicherung in einer Datenbank, z. B. der ungefähren Erhöhung der Reihenfolge und der kostenlosen Codierung ihrer Erstellungszeit. Der Schlüssel für Ihren Anwendungsfall ist, dass sie so konzipiert sind, dass sie die Eindeutigkeit mit hoher Wahrscheinlichkeit garantieren, selbst wenn sie auf verschiedenen Maschinen generiert werden.

Wenn Sie sich nun allgemein auf das Feld _id beziehen, benötigen wir keine Eindeutigkeit für alle Sammlungen, sodass die alte _id sicher wiederverwendet werden kann. Wenn Sie beispielsweise zwei Sammlungen haben colorsund fruitsbeide gleichzeitig ein Objekt wie haben könnten {_id: 'orange'}.

Wenn Sie mehr darüber erfahren möchten, wie ObjectIds erstellt werden, finden Sie hier die Spezifikation: http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification

mstearn
quelle
11

Falls jemand Probleme mit doppelten Mongo-Objekt-IDs hat, sollten Sie wissen, dass es trotz der Unwahrscheinlichkeit von Dups in Mongo selbst möglich ist, doppelte _ids mit PHP in Mongo zu generieren.

Der Anwendungsfall, in dem dies für mich regelmäßig passiert ist, ist, wenn ich einen Datensatz durchlaufe und versuche, die Daten in eine Sammlung einzufügen.

Das Array, das die Injektionsdaten enthält, muss bei jeder Iteration explizit zurückgesetzt werden - auch wenn Sie den Wert _id nicht angeben. Aus irgendeinem Grund fügt der INSERT-Prozess dem Array die Mongo _id hinzu, als wäre es eine globale Variable (auch wenn das Array keinen globalen Bereich hat). Dies kann sich auch dann auf Sie auswirken, wenn Sie die Einfügung in einem separaten Funktionsaufruf aufrufen, bei dem normalerweise zu erwarten ist, dass die Werte des Arrays nicht auf die aufrufende Funktion zurückgesetzt werden.

Hierfür gibt es drei Lösungen:

  1. Sie können unset()das Feld _id aus dem Array entfernen
  2. Sie können das gesamte Array bei array()jedem Durchlaufen Ihres Datasets neu initialisieren
  3. Sie können den _id-Wert explizit selbst definieren (achten Sie darauf, ihn so zu definieren, dass Sie selbst keine Dups generieren).

Ich vermute, dass dies ein Fehler in der PHP-Oberfläche ist und nicht so sehr ein Problem mit Mongo, aber wenn Sie auf dieses Problem stoßen, deaktivieren Sie einfach die _id und es sollte Ihnen gut gehen.

DenverMatt
quelle
Siehe hier: php.net/manual/en/mongocollection.insert.php : "Hinweis: Wenn der Parameter keinen _id-Schlüssel oder keine Eigenschaft hat, wird eine neue MongoId-Instanz erstellt und ihm zugewiesen. Dieses spezielle Verhalten bedeutet nicht dass der Parameter als Referenz übergeben wird. ", es ist eine Funktion, kein Fehler, es soll so sein
Oliver Konig
1
Ich verstehe das Szenario, das Sie hier beschreiben, nicht. Vielleicht könnten Sie einen Code anzeigen, der den Fehler aufweist?
Mark Amery
-7

Es gibt keinerlei Garantie für die Eindeutigkeit von ObjectId in verschiedenen Sammlungen. Selbst wenn es wahrscheinlich sehr unwahrscheinlich ist, wäre es ein sehr schlechtes Anwendungsdesign, das sich auf die Einzigartigkeit von _id in allen Sammlungen stützt.

Man kann dies leicht in der Mongo-Schale testen:

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" }

Verlassen Sie sich also absolut nicht darauf, dass _id in allen Sammlungen eindeutig ist, und verlassen Sie sich nicht darauf, da Sie die ObjectId-Generierungsfunktion nicht steuern.

Es ist möglich, etwas zu erstellen, das eher einer UUID ähnelt. Wenn Sie dies manuell tun, können Sie die Einzigartigkeit besser garantieren.

Denken Sie daran, dass Sie Objekte unterschiedlicher "Typen" in dieselbe Sammlung aufnehmen können. Warum also nicht einfach Ihre beiden "Tabellen" in dieselbe Sammlung einfügen? Sie würden sich den gleichen ID-Raum teilen und somit garantiert einzigartig sein. Der Wechsel von "Interessent" zu "Registriert" wäre ein einfaches Umblättern eines Feldes ...

Slacy
quelle
1
Ich denke, Sie verwechseln das Feld _id im Allgemeinen mit dem ObjectID-Typ. Der ObjectID-Typ wurde speziell für die Eindeutigkeit mit dem Ziel entwickelt, dass er wie eine UUID behandelt werden kann. Das Feld _id kann jedoch ein beliebiger Typ sein und garantiert nur dann die Eindeutigkeit einer einzelnen Auflistung, wenn Sie andere Typen für den Schlüssel verwenden, z. B. eine Zeichenfolge in Ihrem Beispiel.
mstearn
@mstearn (Nitpick) Die Vorstellung, dass eine UUID von Natur aus eindeutig ist, ist fehlerhaft. Eine gute UUID- / Sequenzgenerierungsstrategie kann die Kollision unwahrscheinlich machen, muss jedoch eindeutige Generatoren (z. B. eindeutige Standorte) berücksichtigen, um eine absolute Eindeutigkeit zwischen den Generatoren zu gewährleisten . Zugegeben, die meisten haben so niedrige Wahrscheinlichkeiten, dass dies keine Rolle spielt :-) GUID . Ein Problem, das jedoch auftritt, ist das Duplizieren / Kopieren von IDs anstelle einer neuen Generation.
1
@pst: MongoDBs ObjectIDs enthalten sowohl die PID des Generierungsprozesses als auch einige Bytes, die auf einem Hash des Hostnamens basieren. In Kombination mit einem Zeitstempel und einem inkrementierenden Zähler ist es äußerst wahrscheinlich, dass zwei separat generierte ObjectIDs global / universell eindeutig sind. Wie Sie bereits sagten, gilt dies natürlich nur für frisch generierte ObjectIDs.
mstearn
1
Ich beziehe mich auf den ObjectId-Typ. Keine Angabe eines Zeichenfolgenwerts für '_id'. Natürlich werden sie gleich sein und Konflikte verursachen, wenn Sie sie manuell auf genau dieselbe Zeichenfolge einstellen.
Anthony Jack
Ja, ich habe die Dinge in meinem Beitrag geklärt. _ids sind sicherlich nicht eindeutig, und da Sie die ObjectId-Generierungsfunktion nicht steuern, ist es wahrscheinlich eine schlechte Idee, sich darauf zu verlassen.
Slacy