MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Gérer les requêtes lentes dans MongoDB

Lorsqu'elle est en production, une application doit fournir une réponse rapide à l'utilisateur dans le but d'améliorer l'interaction de l'utilisateur avec votre application. Parfois, cependant, les requêtes de base de données peuvent commencer à prendre du retard, ce qui prend une latence plus longue pour qu'une réponse atteigne l'utilisateur ou plutôt l'opération de débit s'est terminée en raison du dépassement du délai d'attente moyen défini.

Dans ce blog, nous allons apprendre comment identifier ces problèmes dans MongoDB, comment les résoudre chaque fois qu'ils surviennent et quelles sont les stratégies possibles à entreprendre pour que cela ne se reproduise plus.

Le plus souvent, ce qui entraîne des réponses lentes aux requêtes est une capacité de processeur dégradée qui ne peut pas supporter l'ensemble de travail sous-jacent. Le jeu de travail dans ce cas est la quantité de données et d'index qui seront soumis à une instance de débit donc active à ce moment-là. Ceci est particulièrement pris en compte dans la planification des capacités lorsque l'on s'attend à ce que la quantité de données impliquées augmente avec le temps et le nombre d'utilisateurs qui interagissent avec votre plate-forme.

Identifier un problème de requête lente

Il existe deux façons d'identifier les requêtes lentes dans MongoDB.

  1. Utiliser le profileur
  2. Utilisation de l'assistant db.currentOp()

Utiliser le profileur MongoDB

Le profileur de base de données dans MongoDB est un mécanisme permettant de collecter des informations détaillées sur les commandes de base de données exécutées sur une instance mongod en cours d'exécution, à savoir :les opérations de débit (créer, lire, mettre à jour et supprimer) et les commandes de configuration et d'administration.

Le profileur utilise une collection limitée nommée system.profile où il écrit toutes les données. Cela signifie que lorsque la collection est pleine en termes de taille, les documents les plus anciens sont supprimés pour laisser place aux nouvelles données.

Le profileur est désactivé par défaut mais selon le niveau de profilage, on peut l'activer sur une base de données ou par instance. Les niveaux de profilage possibles sont :

  • 0 :le profileur est désactivé et ne collecte donc aucune donnée.
  • 1 - le profileur collecte des données pour les opérations qui prennent plus de temps que la valeur de slowms
  • 2- le profileur collecte des données pour toutes les opérations.

 Cependant, l'activation du profilage génère un impact sur les performances de la base de données et de l'utilisation du disque, en particulier lorsque le niveau de profilage est défini sur 2 . Il convient de prendre en compte toutes les implications sur les performances avant d'activer et de configurer le profileur sur un déploiement de production.

Pour définir le profilage, nous utilisons l'assistant db.setProfilingLevel() tel que :

db.setProfilingLevel(2)

Un exemple de document qui sera stocké dans la collection system.profile sera :

{ "was" : 0, "slowms" : 100, "sampleRate" : 1.0, "ok" : 1 }

La paire clé-valeur « ok » :1 indique que l'opération a réussi, tandis que slowms est le seuil de temps en millisecondes qu'une opération doit prendre et est par défaut de 100 ms.

Pour modifier cette valeur

db.setProfilingLevel(1, { slowms: 50 })

Pour rechercher des données dans l'exécution de la collecte system.profile :

db.system.profile.find().pretty()

Utilisation de db.currentOp()helper

Cette fonction répertorie les requêtes en cours d'exécution avec des informations très détaillées telles que la durée de leur exécution. Sur un shell mongo en cours d'exécution, vous exécutez le commentaire par exemple :

db.currentOp({"secs_running":{$gte :5}}) 

Où secs_running est la stratégie de filtrage afin que seules les opérations qui ont pris plus de 5 secondes à s'exécuter soient renvoyées, ce qui réduit la sortie. Ceci est souvent utilisé lorsque l'état du processeur peut être évalué à 100 % en raison de l'impact négatif sur les performances qu'il peut avoir sur la base de données. Ainsi, en modifiant les valeurs, vous saurez quelles requêtes prennent du temps à s'exécuter.

Les documents renvoyés ont comme clés d'intérêt :

  • requête  :ce que la requête implique
  • actif  : si la requête est toujours en cours.
  • ns :nom de la collection sur laquelle la requête doit être exécutée
  • secs_running  : durée de la requête jusqu'à présent en secondes

En mettant en évidence les requêtes qui prennent du temps, vous avez identifié ce qui surcharge le CPU.

Interpréter les résultats et résoudre les problèmes

 Comme nous l'avons décrit ci-dessus, la latence des requêtes dépend fortement de la quantité de données impliquées, ce qui entraînerait sinon des plans d'exécution inefficaces. C'est-à-dire que, par exemple, si vous n'utilisez pas d'index dans votre collection et que vous souhaitez mettre à jour certains enregistrements, l'opération doit parcourir tous les documents plutôt que de filtrer uniquement ceux qui correspondent à la spécification de la requête. Logiquement, cela prendra plus de temps, ce qui entraînera une requête lente. Vous pouvez examiner un plan d'exécution inefficace en exécutant :  expliquer(‘executionStats’) qui fournit des statistiques sur les performances de la requête. À partir de ce point, vous pouvez apprendre comment la requête utilise l'index en plus de fournir un indice si l'index est optimal.

Si l'assistant d'explication revient

{

   "queryPlanner" : {

         "plannerVersion" : 1,

         ...

         "winningPlan" : {

            "stage" : "COLLSCAN",

            ...

         }

   },

   "executionStats" : {

      "executionSuccess" : true,

      "nReturned" : 3,

      "executionTimeMillis" : 0,

      "totalKeysExamined" : 0,

      "totalDocsExamined" : 10,

      "executionStages" : {

         "stage" : "COLLSCAN",

         ...

      },

      ...

   },

   ...

}

queryPlanner.winningPlan.stage :la valeur de la clé COLLSCAN indique que le mongod a dû analyser l'intégralité du document de collecte pour identifier les résultats. Cela devient donc une opération coûteuse, ce qui entraîne des requêtes lentes.

executionStats.totalKeysExamined:0 signifie que la collection n'utilise pas de stratégie d'indexation

Pour une requête donnée, le nombre de documents concernés doit être proche de zéro. Si le nombre de documents est assez important, il y a deux possibilités :

  1. Ne pas utiliser l'indexation avec la collection
  2. Utiliser un index qui n'est pas optimal.

Pour créer un index pour une collection, exécutez la commande : 

db.collection.createIndex( { quantity: 1 } )

Où la quantité est un exemple de champ que vous avez sélectionné pour être optimal pour la stratégie d'indexation.

Si vous souhaitez en savoir plus sur l'indexation et sur la stratégie d'indexation à utiliser, consultez ce blog

Conclusion

La dégradation des performances de la base de données peut être facilement illustrée par des requêtes lentes, ce qui est la moindre attente que nous voudrions que les utilisateurs de la plate-forme rencontrent. On peut identifier les requêtes lentes dans MongoDB en activant le profileur et en le configurant selon ses spécifications ou en exécutant db.currentOp() sur une instance mongod en cours d'exécution.

En examinant les paramètres de temps sur le résultat renvoyé, nous pouvons identifier les requêtes en retard. Après avoir identifié ces requêtes, nous utilisons l'assistant d'explication sur ces requêtes pour obtenir plus de détails, par exemple si la requête utilise un index.

Sans indexation, les opérations deviennent coûteuses car de nombreux documents doivent être scannés avant d'appliquer les modifications. Avec ce revers, le processeur sera surchargé, ce qui entraînera des requêtes lentes et des pics de processeur croissants.

L'erreur majeure qui conduit à des requêtes lentes est une planification d'exécution inefficace qui peut être facilement résolue en utilisant un index avec la collection impliquée.