Possibilité de dupliquer les Mongo ObjectId générés dans deux collections différentes?

188

Est-il possible que le même Mongo ObjectId exact soit généré pour un document dans deux collections différentes? Je me rends compte que c'est vraiment très improbable, mais est-ce possible?

Sans être trop précis, la raison pour laquelle je pose la question est qu'avec une application sur laquelle je travaille, nous montrons des profils publics d'élus que nous espérons convertir en utilisateurs à part entière de notre site. Nous avons des collections séparées pour les utilisateurs et les élus qui ne sont pas actuellement membres de notre site. Il existe divers autres documents contenant diverses données sur les élus qui sont tous renvoyés à la personne utilisant leur ObjectId élu.

Après avoir créé le compte, nous mettons toujours en évidence les données associées à l'élu, mais elles font désormais également partie de la collection d'utilisateurs avec un ObjectId d'utilisateurs correspondant pour mapper leur profil aux interactions avec notre application.

Nous avions commencé à convertir notre application de MySql à Mongo il y a quelques mois et pendant la transition, nous stockons l'ancien identifiant MySql pour ces deux types de données et nous commençons également à stocker le Mongo ObjectId officiel élu dans les utilisateurs. document à renvoyer aux données des élus.

Je réfléchissais simplement à la spécification du nouvel utilisateur ObjectId en tant qu'ObjectId officiel élu précédent pour simplifier les choses, mais je voulais m'assurer qu'il n'était pas possible d'avoir une collision avec un ObjectId utilisateur existant.

Merci pour votre perspicacité.

Edit: Peu de temps après avoir posté cette question, j'ai réalisé que ma solution proposée n'était pas une très bonne idée. Il serait préférable de conserver le schéma actuel que nous avons en place et de le lier simplement au représentant élu '_id' dans le document des utilisateurs.

Anthony Jack
la source
1
J'ai déjà lu cette page. Ironiquement, j'ai en fait lié à la même page dans une réponse précédente. Et j'ai vu la clause de non-responsabilité relative à la "probabilité raisonnablement élevée d'être unique", mais je ne savais pas si la collection insérée avait joué un rôle dans cela. Je suppose que ce dont je ne suis pas sûr, c'est ce que représente exactement la partie ID de processus de 2 octets de ObjectId. Si cela a quelque chose à voir avec la collection, alors il y aurait unicité entre deux documents différents créés exactement au même moment sur la même machine exacte dans des collections différentes.
Anthony Jack
1
L'ID de processus de 2 octets est le pid du processus générant l'ObjectID. À titre d'exemple, voici le code utilisé par pymongo pour générer des ObjectID: github.com/mongodb/mongo-python-driver/blob/master/bson/…
mstearn
Un problème que j'ai rencontré est l'insertion par lots. Je construisais des lots de 10 000 documents et je me heurtais à chaque fois parce que la partie du compteur retournait à chaque fois.
fawce
Je sais que cela faisait un moment, mais les documents 10K ne roulaient pas sur le comptoir. La partie compteur est de trois octets et non de trois chiffres. C'est plus de 16 millions.
Asya Kamsky

Réponses:

326

Réponse courte

Juste pour ajouter une réponse directe à votre question initiale: OUI, si vous utilisez la génération d'ID d'objet BSON, alors pour la plupart des pilotes, les ID seront presque certainement uniques dans les collections. Voir ci-dessous ce que signifie «presque certainement».

Longue réponse

Les ID d'objet BSON générés par les pilotes Mongo DB sont très probablement uniques dans les collections. Ceci est principalement dû aux 3 derniers octets de l'ID, qui pour la plupart des pilotes est généré via un compteur incrémentiel statique. Ce compteur est indépendant de la collection; c'est mondial. Le pilote Java, par exemple, utilise un AtomicInteger statique initialisé de manière aléatoire.

Alors pourquoi, dans la documentation Mongo, disent-ils que les identifiants sont "hautement susceptibles" d'être uniques, au lieu de dire carrément qu'ils seront uniques? Trois possibilités peuvent se produire où vous n'obtiendrez pas un identifiant unique (veuillez me le faire savoir s'il y en a plus):

Avant cette discussion, rappelez-vous que l'ID d'objet BSON se compose de:

[4 octets secondes depuis l'époque, 3 octets de hachage machine, 2 octets d'ID de processus, 3 octets de compteur]

Voici les trois possibilités, vous jugez donc par vous-même de la probabilité d'être dupe:

1) Dépassement du compteur: il y a 3 octets dans le compteur. Si vous insérez plus de 16 777 216 (2 ^ 24) documents en une seule seconde, sur la même machine, dans le même processus, vous pouvez alors déborder les octets du compteur incrémentiel et vous retrouver avec deux ID d'objet qui partagent le même , processus et valeurs de compteur.

2) Compteur non incrémenté: certains pilotes Mongo utilisent des nombres aléatoires au lieu d'incrémenter des nombres pour les octets du compteur. Dans ces cas, il y a une chance de 1/16 777 216 de générer un identifiant non unique, mais seulement si ces deux identifiants sont générés dans la même seconde (c'est-à-dire avant que la section temporelle de l'identifiant ne passe à la seconde suivante), sur le même machine, dans le même processus.

3) Machine et processus de hachage aux mêmes valeurs. Les valeurs d'ID d'ordinateur et d'ID de processus peuvent, dans un scénario hautement improbable, correspondre aux mêmes valeurs pour deux ordinateurs différents. Si cela se produit, et en même temps les deux compteurs sur les deux machines différentes, pendant la même seconde, génèrent la même valeur, alors vous vous retrouverez avec un ID en double.

Ce sont les trois scénarios à surveiller. Les scénarios 1 et 3 semblent hautement improbables, et le scénario 2 est totalement évitable si vous utilisez le bon pilote. Vous devrez vérifier la source du pilote pour en être sûr.

Raj Advani
la source
Le compteur 3 octets ne représente-t-il pas une capacité d'accepter 2 ^ 24 = 16777216 nombre de documents insérés par seconde par processus et par machine?
Forrest Ye
Vous avez tout à fait raison, j'ai accidentellement divisé par deux le nombre de bits - la réponse a été modifiée.
Raj Advani
Depuis que je viens d'entrer dans ce domaine, permettez-moi d'ajouter que certains pilotes (par exemple C), bien qu'utilisant des incréments, n'incrémentent pas de manière atomique, donc de temps en temps, il génère le même oid en raison de la condition de
concurrence
42
Vous avez complètement ignoré le fait que dans 136 ans, vous auriez un autre coup pour générer le même que ObjectIdvous aviez avant tant que le hachage de la machine, l'ID de processus et le compteur se révèlent tous identiques
jamylak
29
@jamylak Nous nous occuperons de ce problème quand il deviendra urgent (ont déclaré ceux qui ont standardisé les formats de date AAMMJJ dans les années 70)
Philipp
15

Les ObjectIds sont générés côté client d'une manière similaire à l'UUID, mais avec des propriétés plus intéressantes pour le stockage dans une base de données, telles qu'une augmentation grossière de l'ordre et le codage de leur temps de création gratuitement. L'essentiel pour votre cas d'utilisation est qu'ils sont conçus pour garantir l'unicité à une probabilité élevée même s'ils sont générés sur des machines différentes.

Maintenant, si vous faisiez référence au champ _id en général, nous n'avons pas besoin d'unicité entre les collections, il est donc prudent de réutiliser l'ancien _id. À titre d'exemple concret, si vous avez deux collections, colorset fruits, les deux pourraient avoir simultanément un objet comme {_id: 'orange'}.

Si vous souhaitez en savoir plus sur la création des ObjectIds, voici la spécification: http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification

mstearn
la source
12

Au cas où quelqu'un aurait des problèmes avec des ObjectID Mongo dupliqués, vous devez savoir que malgré l'improbabilité que des doublons se produisent dans Mongo lui-même, il est possible d'avoir des _id dupliqués générés avec PHP dans Mongo.

Le cas d'utilisation où cela s'est produit avec régularité pour moi est lorsque je parcours un ensemble de données et que je tente d'injecter les données dans une collection.

Le tableau qui contient les données d'injection doit être explicitement réinitialisé à chaque itération, même si vous ne spécifiez pas la valeur _id. Pour une raison quelconque, le processus INSERT ajoute le Mongo _id au tableau comme s'il s'agissait d'une variable globale (même si le tableau n'a pas de portée globale). Cela peut vous affecter même si vous appelez l'insertion dans un appel de fonction distinct où vous vous attendriez normalement à ce que les valeurs du tableau ne persistent pas dans la fonction appelante.

Il existe trois solutions à cela:

  1. Vous pouvez unset()le champ _id du tableau
  2. Vous pouvez réinitialiser l'ensemble du tableau à array()chaque fois que vous parcourez votre ensemble de données
  3. Vous pouvez définir explicitement la valeur _id vous-même (en prenant soin de la définir de manière à ne pas générer de dups vous-même).

Je suppose que c'est un bogue dans l'interface PHP, et pas tellement un problème avec Mongo, mais si vous rencontrez ce problème, désactivez simplement le _id et tout devrait bien se passer.

DenverMatt
la source
voir ici: php.net/manual/en/mongocollection.insert.php : "Remarque: Si le paramètre n'a pas de clé ou de propriété _id, une nouvelle instance de MongoId sera créée et lui sera assignée. Ce comportement spécial ne signifie pas que le paramètre est passé par référence. ", c'est une fonctionnalité, pas un bogue, c'est censé être comme ça
Oliver Konig
1
Je ne comprends pas le scénario que vous décrivez ici; peut-être pourriez-vous montrer du code qui présente le bogue?
Mark Amery
-8

Il n'y a aucune garantie sur l'unicité d'ObjectId dans les collections. Même si c'est probablement très improbable, il s'agirait d'une conception d'application très médiocre qui reposerait sur l'unicité de _id dans les collections.

On peut facilement tester cela dans le 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" }

Donc, ne comptez absolument pas sur le fait que _id est unique dans les collections, et comme vous ne contrôlez pas la fonction de génération ObjectId, ne vous y fiez pas.

Il est possible de créer quelque chose qui ressemble plus à un uuid, et si vous le faites manuellement, vous pourriez avoir une meilleure garantie d'unicité.

N'oubliez pas que vous pouvez mettre des objets de "types" différents dans la même collection, alors pourquoi ne pas simplement mettre vos deux "tables" dans la même collection. Ils partageraient le même espace _id, et seraient donc garantis uniques. Passer de "prospectif" à "enregistré" serait un simple retournement d'un champ ...

slacy
la source
1
Je pense que vous pouvez confondre le champ _id en général avec le type ObjectID. Le type ObjectID a été spécialement conçu pour l'unicité dans le but qu'il puisse être traité comme un UUID. Cependant, le champ _id peut être de n'importe quel type et ne garantit l'unicité sur une seule collection que si vous utilisez d'autres types pour la clé, comme une chaîne dans votre exemple.
mstearn le
@mstearn (Nitpick) La notion selon laquelle un UUID est intrinsèquement unique est erronée. Une bonne stratégie de génération d'UUID / séquence peut rendre la collision peu probable, mais elle doit prendre en compte des générateurs uniques (par exemple des emplacements uniques) pour garantir l'unicité absolue entre les générateurs. Certes, la plupart ont des probabilités si faibles qu'elles ne sont d'aucune utilité :-) GUID . Un problème qui se pose cependant est la duplication / copie des identifiants au lieu d'une nouvelle génération.
1
@pst: Les ObjectID de MongoDBs incluent à la fois le pid du processus de génération et certains octets basés sur un hachage du nom d'hôte. Celles-ci, combinées à un horodatage et à un compteur incrémentiel, font qu'il est extrêmement probable que deux ObjectID générés séparément soient uniques au monde. Bien sûr, comme vous l'avez dit, cela ne s'applique qu'aux ObjectID fraîchement générés.
mstearn le
1
Je fais référence au type ObjectId. Ne pas spécifier de valeur de chaîne pour «_id». Bien sûr, ils seront identiques et entreront en conflit si vous les définissez manuellement sur la même chaîne.
Anthony Jack
Ouais, j'ai clarifié les choses dans mon post. Les _id ne sont certainement pas uniques, et comme vous ne contrôlez pas la fonction de génération ObjectId, c'est probablement une mauvaise idée de s'y fier.
slacy