Aller au contenu

Requêtes sous-optimales et profiling#

Comprendre les requêtes sous-optimales#

Requêtes sous-optimales#

  • Définition : requêtes lentes ou consommant trop de ressources
  • Requêtes avec des scans de collection entière (table scans)
  • Requêtes avec des jointures mal optimisées
  • Requêtes avec des filtres, des tris ou des agrégations inefficaces
  • Exemple de requête lente sans index : db.collection.find({age: {$gte: 18}})

Impact sur les performances globales de la base de données#

  • Ralentissement de l'exécution des autres requêtes
  • Augmentation de la charge CPU et de l'utilisation de la mémoire
  • Diminution de la capacité à répondre aux demandes en temps réel

Nécessité d'identifier et d'optimiser ces requêtes#

  • Amélioration des performances globales de la base de données
  • Réduction de la consommation de ressources (CPU, mémoire, disque)
  • Meilleure expérience pour les utilisateurs finaux

Activer et configurer le profileur de requêtes#

Activation du profileur :#

  • Utilisez la méthode db.setProfilingLevel(level, options) pour activer le profileur
  • level : niveau de profilage souhaité
  • options : paramètres supplémentaires, tels que slowms

Niveaux de profilage#

  • 0 : désactivé, aucune requête n'est enregistrée
  • 1 : activé pour les requêtes lentes, seulement les requêtes dépassant le seuil slowms sont enregistrées
  • 2 : activé pour toutes les requêtes, toutes les requêtes sont enregistrées

Options : seuil de durée en ms pour le niveau 1#

  • slowms : seuil de durée en millisecondes pour déterminer si une requête est considérée comme lente
  • Exemple : db.setProfilingLevel(1, {slowms: 200})

Exemple#

> db.setProfilingLevel(1, {slowms: 100})
{ "was" : 0, "slowms" : 100, "sampleRate" : 1, "ok" : 1 }

📘 https://www.mongodb.com/docs/manual/tutorial/manage-the-database-profiler/

📘 https://www.mongodb.com/docs/manual/reference/method/db.setProfilingLevel/

📘 https://studio3t.com/knowledge-base/articles/mongodb-query-performance/

Analyser les résultats du profileur#

Récupération des données#

  • Commande : db.system.profile.find().pretty()
  • Affiche les entrées du profileur dans un format lisible

Informations clés : opération, durée, index utilisés, nombre de documents examinés#

  • Opération : type d'opération (ex : query, update, insert, remove)
  • Durée : temps d'exécution de l'opération en millisecondes
  • Index utilisés : index exploités lors de l'exécution de la requête
  • Nombre de documents examinés : quantité de documents traités pour répondre à la requête

Exemple

{
    "op" : "query",
    "ns" : "testDB.products",
    "query" : { "price" : { "$gte" : 100 } },
    "cursorid" : NumberLong("4512952734067705982"),
    "keysExamined" : 0,
    "docsExamined" : 2000,
    "nreturned" : 50,
    "responseLength" : 10234,
    "millis" : 120,
    "planSummary" : "COLLSCAN",
    "execStats" : { ... },
    "ts" : ISODate("2023-04-17T12:00:00.000Z"),
    "client" : "192.168.0.100",
    "allUsers" : [ ],
    "user" : ""
}

Analyse des données pour déterminer les requêtes sous-optimales#

  • Rechercher les entrées avec une longue durée (ex : > 100 ms) db.system.profile.find({ "millis": { "$gt": 100 } }).pretty()
  • Identifier les requêtes sans index (ex : "planSummary" : "COLLSCAN" au lieu de "IDHACK") db.system.profile.find({ "planSummary": "COLLSCAN" }).pretty()
  • Requêtes avec un grand nombre de documents examinés db.system.profile.find({ "docsExamined": { "$gt": 1000 } }).pretty()
  • Requete combinée db.system.profile.find({ "millis": { "$gt": 100 }, "planSummary": "COLLSCAN" }).pretty()

Exemple (pour la requête ci-dessus)

  • elle n'utilise pas d'index
  • elle examine 2000 documents
  • elle prend un temps d'exécution de 120 ms,
  • ... tout cela indique une requête sous-optimale!

Optimisation des requêtes#

Utilisation d'index appropriés pour accélérer les requêtes#

  • Création d'index : db.collection.createIndex(keys, options)
  • Exemple d'index simple : db.users.createIndex({age: 1})
  • Exemple d'index composé : db.users.createIndex({age: 1, city: 1})
  • Importance de choisir les bons index pour les requêtes fréquentes
  • Surveiller les performances des index avec db.collection.aggregate([{$indexStats: {}}])

Réécriture des requêtes pour améliorer les performances#

  • Utiliser des opérateurs efficaces, tels que $in et $elemMatch
  • Exemple de requête optimisée avec $in : db.users.find({city: {$in: ["Paris", "London"]}})
  • Préférer l'usage de $and au lieu de $or lorsque cela est possible
  • Exemple de requête optimisée avec $and : db.users.find({$and: [{age: {$gt: 25}}, {city: "Paris"}]})
  • Éviter les opérations de tri coûteuses en utilisant des index

Limitation des champs renvoyés pour réduire la charge de la bande passante#

  • Utiliser le second argument de la méthode find pour spécifier les champs à renvoyer
  • Exemple : db.users.find({age: {$gt: 25}}, {name: 1, city: 1, _id: 0})
  • Utiliser 1 pour inclure un champ, et 0 pour l'exclure (sauf pour _id qui est inclus par défaut)
  • Renvoyer uniquement les données nécessaires pour réduire la charge sur le réseau et le serveur