46 votes

Dois-je implémenter l'auto-incrémentation dans MongoDB ?

Je suis en train de passer de MySQL à MongoDB. Une architecture qui m'est familière pour un système très basique users aurait une auto-incrémentation de la table uid . Voir la documentation de Mongo pour ce cas d'utilisation. .

Je me demande si c'est la meilleure décision architecturale. D'un point de vue UX, j'aime avoir des UID comme références externes, par exemple dans des URL plus courtes : http://example.com/users/12345

Existe-t-il une troisième voie ? Quelqu'un dans l'IRC Freenode's #mongodb a suggéré de créer une série d'identifiants et de les mettre en cache. Je ne suis pas sûr de savoir comment mettre cela en œuvre, ou s'il y a une autre voie à suivre. Je n'ai même pas nécessairement besoin de la fonction _id lui-même pour être incrémenté de cette façon. Tant que le users ont tous un numéro unique uid dans le document, j'en serais heureux.

93voto

ruslan Points 5754

Je ne suis pas du tout d'accord avec l'auteur de la réponse choisie qui dit que Il n'y a pas d'auto-incrémentation de l'id dans MongoDB et il y a de bonnes raisons. . Nous ne savons pas pourquoi 10gen n'a pas encouragé l'utilisation d'identifiants auto-incrémentés. C'est de la spéculation. Je pense que 10gen a fait ce choix parce qu'il est simplement plus facile d'assurer l'unicité des ID de 12 octets dans un environnement en cluster. C'est une solution par défaut qui convient à la plupart des nouveaux arrivants et qui augmente donc l'adoption du produit, ce qui est bon pour les affaires de 10gen.

Laissez-moi maintenant vous parler de mon expérience avec les ObjectIds dans un environnement commercial.

Je suis en train de créer un réseau social. Nous avons environ 6 millions d'utilisateurs et chaque utilisateur a environ 20 amis.

Imaginons maintenant que nous ayons une collection qui stocke les relations entre les utilisateurs (qui suit qui). Cela ressemble à ceci

_id : ObjectId
user_id : ObjectId
followee_id : ObjectId

sur lequel nous avons un indice composite unique {user_id, followee_id} . Nous pouvons estimer la taille de cet index à 12*2*6M*20 = 2GB. Voilà l'index pour la recherche rapide des personnes que je suis. Pour une recherche rapide des personnes qui me suivent, j'ai besoin d'un index inverse. Cela représente 2 Go supplémentaires.

Et ce n'est que le début. Je dois porter ces identifiants partout. Nous avons un cluster d'activité où nous stockons votre fil d'actualité. C'est chaque événement que vous ou vos amis font. Imaginez l'espace que ça prend.

Et finalement, l'un de nos ingénieurs a pris une décision inconsciente et a décidé de stocker les références sous forme de chaînes qui représentent l'ObjectId, ce qui double sa taille.

Que se passe-t-il si un index ne tient pas dans la RAM ? Rien de bon, dit 10gen :

Lorsqu'un index est trop volumineux pour tenir dans la RAM, MongoDB doit lire l'index depuis le disque, ce qui est une opération beaucoup plus lente que la lecture depuis la RAM. Gardez à l'esprit qu'un index tient dans la RAM lorsque votre serveur dispose de RAM pour l'index combiné au reste de l'ensemble de travail.

Cela signifie que les lectures sont lentes. La contention des verrous augmente. Les écritures deviennent également plus lentes. Voir la contention des verrous dans 80% des cas n'est plus un choc pour moi.

En un rien de temps, vous vous retrouvez avec un cluster de 460 Go que vous devez diviser en fragments et qui est assez difficile à manipuler.

Facebook utilise un nom long de 64 bits comme identifiant d'utilisateur :) Il y a une raison à cela. Vous pouvez générer des identifiants séquentiels

  • en utilisant Le conseil de 10gen .
  • en utilisant mysql comme stockage des compteurs (si vous êtes préoccupé par la vitesse, jetez un coup d'œil à handlersocket )
  • en utilisant le service de génération d'ID que vous avez créé ou en utilisant quelque chose comme Flocon de neige par Twitter.

Voici donc le conseil général que je donne à tout le monde. Faites en sorte que vos données soient aussi petites que possible. Lorsque vous vous développerez, cela vous épargnera de nombreuses nuits blanches.

20 votes

Au cas où vous vous poseriez la question, 10gen sont les créateurs originaux de MongoDB et en 2013, a changé son nom en MongoDB Inc.

0 votes

Bien que cela ne soit pas tout à fait en rapport avec votre question/mandat/conseil, vous pouvez toujours faire ce qui suit _id: {user_id, followee_id} ou {_id: user_id, followee_id: [array of <followee_id>] }

22voto

kheya Points 2435

Josh, Il n'y a pas d'auto-incrémentation des identifiants dans MongoDB et il y a de bonnes raisons. Je dirais qu'il faut utiliser des ObjectIds qui sont uniques dans le cluster.

Vous pouvez ajouter l'incrémentation automatique par une collection de séquences et utiliser findAndModify pour obtenir le prochain identifiant à utiliser. Cela ajoutera certainement des complexités à votre application et peut également affecter la capacité de diviser votre base de données.

Tant que vous pouvez garantir que vos identifiants générés seront uniques, tout ira bien. Mais le mal de tête sera là.

Vous pouvez consulter ce message pour plus d'informations sur cette question dans le groupe Google dédié à MongoDB :

http://groups.google.com/group/mongodb-user/browse_thread/thread/f57b712b2aae6f0b/b4315285e689b9a7?lnk=gst&q=projapati#b4315285e689b9a7

J'espère que cela vous aidera.

Merci

18voto

Gates VP Points 26481

Il y a donc un problème fondamental avec les ID "auto-incrémentés". Lorsque vous avez 10 serveurs différents ( tessons dans MongoDB), qui choisit l'identifiant suivant ?

Si vous voulez un ensemble unique d'identifiants s'incrémentant automatiquement, vous devez avoir une autorité unique pour choisir ces identifiants. Avec MySQL, c'est généralement assez facile, car un seul serveur accepte les écritures. Mais les grands déploiements de MongoDB utilisent le sharding qui ne dispose pas de cette "autorité centrale".

MongoDB, utilise 12 octets ObjectIds afin que chaque serveur puisse créer de nouveaux documents de manière unique sans dépendre d'une autorité unique.

Voici donc la grande question : "pouvez-vous vous permettre d'avoir une seule autorité" ?

Si c'est le cas, vous pouvez utiliser findAndModify pour garder la trace de la "dernière ID la plus élevée" et vous pouvez ensuite insérer avec cela.

C'est le processus décrit dans votre lien. La faiblesse évidente ici est que vous devez techniquement faire deux écritures pour chaque insertion. Cela peut ne pas être très bien adapté, vous voudrez probablement l'éviter sur les données avec un taux d'insertion élevé. Cela peut fonctionner pour les utilisateurs, mais probablement pas pour le suivi des clics.

0 votes

Le suivi des clics est un mauvais exemple, personne ne se soucie de savoir si l'_id est un ObjectId dans ce cas. Vous pouvez même utiliser un _id composé pour le suivi des clics.

12voto

Andreas Jung Points 1

Il n'y a rien de tel qu'une auto-incrémentation dans MongoDB mais vous pouvez stocker vos propres compteurs dans une collection dédiée et $inc la valeur correspondante du compteur si nécessaire. Comme $inc est une opération atomique, vous ne verrez pas de doublons.

0 votes

Peut-être que je vous comprends mal, mais je ne suis pas sûr que cela résout mon problème particulier, puisque vous devrez vous référer à ce compteur à chaque insertion, et puisque les insertions ne sont pas atomiques, il n'y a aucun moyen de garantir l'unicité.

0 votes

Vous pouvez aller chercher un nouvel identifiant en une seule opération atomique et l'utiliser dans les mises à jour ultérieures... de toute façon... c'est ce que vous obtenez de MongoDB - rien de plus, rien de moins...

1 votes

Je suis d'accord avec cette réponse. Le site $inc est atomique même si l'insertion ne l'est pas. Nous pouvons utiliser {new: true} pour récupérer la nouvelle valeur du compteur. Par exemple : counter.findByIdAndUpdate({_id: counterName}, {$inc: {seq: 1}}, {new: true})

4voto

Gabe Rainbow Points 751

L'ObjectId par défaut de Mongo -- celui utilisé dans le champ _id -- est incrémenté.

Mongo utilise un horodatage ( secondes depuis l'époque Unix) comme première portion de 4 octets de sa composition 4-3-2-3, très similaire (si ce n'est exactement) à la même composition qu'un UUID version 1. Et cet ObjectId est généré au moment de l'insertion (si aucun autre type d'_id n'est fourni par l'utilisateur/client).

L'ObjectId est donc de nature ordinale ; de plus, le tri par défaut est basé sur cet horodatage incrémentiel.

On peut considérer qu'il s'agit d'une version actualisée des identifiants auto-incrémentés (index++) utilisés dans de nombreuses bases de données.

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