202 votes

Possibilité de générer des doublons d'identifiants d'objets Mongo dans deux collections différentes ?

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

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

Après la création du compte, nous mettons toujours en évidence les données associées à l'élu, mais il fait désormais partie de la collection des utilisateurs avec un ObjectId correspondant pour associer son profil aux interactions avec notre application.

Nous avons 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 l'identifiant d'objet Mongo de l'élu dans le document des utilisateurs pour le faire correspondre aux données de l'élu.

Je pensais simplement spécifier le nouvel ObjectId de l'utilisateur comme l'ObjectId de l'ancien élu pour simplifier les choses, mais je voulais m'assurer qu'il n'était pas possible d'avoir une collision avec un ObjectId d'utilisateur existant.

Merci de votre perspicacité.

Edit : Peu après avoir posté cette question, j'ai réalisé que la solution que je proposais n'était pas une très bonne idée. Il serait préférable de conserver le schéma actuel que nous avons mis en place et de créer un lien vers l'élu '_id' dans le document des utilisateurs.

0 votes

1 votes

J'ai déjà lu cette page. Ironiquement, j'ai fait un lien vers la même page dans une réponse précédente. Et j'ai bien vu l'avertissement "probabilité raisonnablement élevée d'être unique", mais je n'étais pas sûr que la collection dans laquelle il est inséré joue un rôle à cet égard. Je suppose que ce que je ne sais pas, c'est ce que représente exactement la portion de 2 octets de l'ID de processus de l'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 dans des collections différentes.

1 votes

L'id de processus de 2byte est le pid du processus générant l'ObjectID. A titre d'exemple, voici le code que pymongo utilise pour générer les ObjectIDs : github.com/mongodb/mongo-python-driver/blob/master/bson/

342voto

Raj Advani Points 1166

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 conducteurs les identifiants seront très certainement uniques dans toutes les collections. Voir ci-dessous ce que signifie "presque certainement".

Réponse longue

Les ID d'objets BSON générés par les pilotes de la base de données Mongo ont de fortes chances d'être uniques dans toutes les collections. Ceci est principalement dû aux 3 derniers octets de l'ID, qui sont les suivants pour la plupart des conducteurs est généré par un compteur statique à incrémentation. Ce compteur est indépendant de la collection ; il est global. Le pilote Java, par exemple, utilise un AtomicInteger statique initialisé de manière aléatoire.

Alors pourquoi, dans la documentation de Mongo, dit-on que les ID sont "hautement susceptibles" d'être uniques, au lieu de dire carrément qu'ils seront uniques ? Trois possibilités peuvent se présenter où vous n'obtiendrez pas un ID unique (faites-moi savoir s'il y en a d'autres) :

Avant cette discussion, rappelons que l'ID d'objet BSON se compose de :

[4 octets de secondes depuis l'époque, 3 octets de hachage de la machine, 2 octets d'identification du processus, 3 octets de compteur].

Voici les trois possibilités, afin que vous puissiez juger par vous-même de la probabilité d'obtenir un double :

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 dépasser les octets du compteur et vous retrouver avec deux ID d'objet qui partagent la même heure, la même machine, le même processus et les mêmes valeurs de compteur.

2) Compteur non incrémentiel : certains pilotes Mongo utilisent des nombres aléatoires au lieu de nombres incrémentiels pour les octets du compteur. Dans ces cas, il y a 1/16 777 216 chances 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 soit mise à jour à la seconde suivante), sur la même machine, dans le même processus.

3) Hachage de la machine et du processus aux mêmes valeurs. Les valeurs de l'ID de la machine et de l'ID du processus peuvent, dans un scénario hautement improbable, correspondre aux mêmes valeurs pour deux machines différentes. Si cela se produit, et qu'au même moment 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 dupliqué.

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

1 votes

Le compteur de 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 par machine ?

0 votes

Vous avez tout à fait raison, j'ai accidentellement divisé par deux le nombre de bits - la réponse a été modifiée.

0 votes

Puisque je viens d'intervenir, permettez-moi d'ajouter que certains pilotes (par exemple le C), bien qu'ils utilisent des incréments, n'incrémentent pas de manière atomique, de sorte que de temps en temps, ils génèrent le même oid en raison d'une condition de course.

16voto

mstearn Points 2295

Les ObjectIds sont générés côté client d'une manière similaire à l'UUID mais avec des propriétés plus agréables pour le stockage dans une base de données comme l'ordre approximativement croissant et l'encodage gratuit de leur temps de création. L'élément clé pour votre cas d'utilisation est qu'ils sont conçus pour garantir l'unicité avec une forte probabilité même s'ils sont générés sur différentes machines.

Si vous faites référence au champ _id en général, nous n'exigeons pas l'unicité entre les collections, il est donc possible de réutiliser l'ancien _id en toute sécurité. Pour donner un exemple concret, si vous avez deux collections, colors y fruits les deux peuvent avoir simultanément un objet comme {_id: 'orange'} .

Si vous voulez en savoir plus sur la façon dont les ObjectIds sont créés, voici la spécification : http://www.mongodb.org/display/DOCS/Object+IDs#ObjectIDs-BSONObjectIDSpecification

12voto

DenverMatt Points 87

Au cas où quelqu'un aurait des problèmes avec des doublons d'identifiants d'objets Mongo, vous devez savoir qu'en dépit de l'improbabilité de doublons dans Mongo lui-même, il est possible d'avoir des _id en double générés par PHP dans Mongo.

Le cas d'utilisation où cela se produit régulièrement pour moi est lorsque je boucle un ensemble de données et que j'essaie 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. F

Il existe trois solutions à ce problème :

  1. Vous pouvez unset() le champ _id du tableau
  2. Vous pouvez réinitialiser l'ensemble du tableau avec array() à chaque fois que vous bouclez sur 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 vous-même des dups).

Je pense qu'il s'agit d'un bogue dans l'interface PHP, et pas vraiment d'un problème avec Mongo, mais si vous rencontrez ce problème, il suffit de désactiver le _id et tout devrait bien se passer.

0 votes

Voir ici : php.net/manual/fr/mongocollection.insert.php : "Remarque : si le paramètre ne possède pas de clé ou de propriété _id, une nouvelle instance MongoId sera créée et lui sera attribuée. Ce comportement spécial ne signifie pas que le paramètre est passé par référence.", c'est une fonctionnalité, pas un bug, c'est censé être ainsi.

3 votes

Je ne comprends pas le scénario que vous décrivez ici ; peut-être pourriez-vous montrer un code qui présente le bogue ?

-8voto

slacy Points 4417

Il n'y a aucune garantie quant à l'unicité de l'ObjectId dans les collections. Même si c'est très improbable d'un point de vue probabiliste, ce serait une très mauvaise conception de l'application que de s'appuyer sur l'unicité de l'_id entre 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 les _id soient uniques dans toutes les collections, et puisque vous ne contrôlez pas la fonction de génération des ObjectId, ne comptez pas sur elle.

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

Rappelez-vous que vous pouvez mettre des objets de différents "types" dans la même collection, alors pourquoi ne pas simplement mettre vos deux "tables" dans la même collection. Elles partageraient le même espace _id, et seraient donc garanties uniques. Passer de "prospective" à "enregistrée" serait une simple inversion d'un champ...

1 votes

Je pense que vous confondez le champ _id en général avec le type ObjectID. Le type ObjectID a été spécifiquement conçu pour l'unicité avec l'objectif qu'il puisse être traité comme un UUID. Cependant, le champ _id peut être de n'importe quel type et ne garantit l'unicité que sur une seule collection si vous utilisez d'autres types pour la clé, comme une chaîne dans votre exemple.

0 votes

@mstearn (Nitpick) La notion qu'une UUID est par nature unique est défectueux. Une bonne stratégie de génération d'UUID/séquence peut rendre la collision improbable, mais elle doit prendre en compte les facteurs suivants générateurs uniques (par exemple, des emplacements uniques) afin de garantir unicité absolue entre les générateurs. Il est vrai que la plupart des probabilités sont si faibles que cela n'a pas d'importance :-) GUID . Une question qui fait Cependant, la duplication/copie des identifiants au lieu d'une nouvelle génération est un problème.

1 votes

@pst : Les ObjectIDs de MongoDB comprennent à la fois le pid du processus de génération et quelques octets basés sur un hachage du nom d'hôte. Ces éléments, combinés à un horodatage et à un compteur incrémentiel, font qu'il est extrêmement probable que deux ObjectIDs générés séparément soient globalement/universellement uniques. Bien sûr, comme vous l'avez dit, cela ne s'applique qu'aux ObjectIDs fraîchement générés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X