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ée1
: activé pour les requêtes lentes, seulement les requêtes dépassant le seuil slowms sont enregistrées2
: 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, et0
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