MongoDB : index et performances
Comprendre pourquoi une requête est lente et la corriger, depuis mongosh. Les exemples utilisent les collections clients et commandes.
Les index
Section intitulée « Les index »Un index accélère la recherche, au prix d’un peu d’espace et d’écritures plus lentes. Sans index, MongoDB parcourt toute la collection (COLLSCAN).
db.clients.createIndex({ email: 1 }, { unique: true }); // unique : refuse les doublonsdb.commandes.createIndex({ client_id: 1 });db.commandes.createIndex({ statut: 1, passee_le: -1 }); // index composédb.clients.createIndex( { ville: 1 }, { partialFilterExpression: { ville: { $exists: true } } },); // index partieldb.clients.createIndex({ nom: 'text' }); // index de recherche textuelledb.clients.getIndexes();db.clients.dropIndex('email_1');db.clients.totalIndexSize();1 = ordre croissant, -1 = décroissant. Les index sont nommés par défaut d’après leurs champs (email_1).
| Type | Usage |
|---|---|
| Simple, composé | Égalité, comparaisons, tri |
unique |
Garantir l’unicité d’une valeur |
TTL (expireAfterSeconds) |
Suppression automatique des documents expirés (sessions, journaux) |
| Partiel | N’indexe que les documents répondant à un filtre : index plus petit |
| Texte | Recherche par mots ($text) |
| 2dsphere | Requêtes géographiques |
Index TTL : expiration automatique
Section intitulée « Index TTL : expiration automatique »db.sessions.createIndex({ cree_le: 1 }, { expireAfterSeconds: 3600 }); // supprime après 1 heureRègle d’ordre des champs dans un index composé
Section intitulée « Règle d’ordre des champs dans un index composé »Placer d’abord les champs filtrés par égalité, puis ceux utilisés pour le tri, puis ceux filtrés par intervalle (règle « ESR » : Equality, Sort, Range). L’index { statut: 1, passee_le: -1 } sert aux filtres sur statut, ou sur statut et passee_le, mais pas sur passee_le seul.
explain : lire le plan d’exécution
Section intitulée « explain : lire le plan d’exécution »db.commandes.find({ statut: 'payee' }).explain('executionStats');Éléments à regarder dans le résultat :
| Champ | Signification |
|---|---|
winningPlan.stage |
COLLSCAN = parcours complet ; IXSCAN = utilisation d’un index (à l’intérieur de FETCH) |
totalDocsExamined |
Documents lus : idéalement proche de nReturned |
totalKeysExamined |
Entrées d’index lues |
nReturned |
Documents renvoyés |
executionTimeMillis |
Durée en millisecondes |
Comparer avant et après la création d’un index :
db.commandes.find({ statut: 'payee' }).explain('executionStats').executionStats .totalDocsExamined;db.commandes.createIndex({ statut: 1 });db.commandes.find({ statut: 'payee' }).explain('executionStats').executionStats .totalDocsExamined;Utilisation des index
Section intitulée « Utilisation des index »db.commandes.aggregate([{ $indexStats: {} }]); // utilisation de chaque index (accès, date du dernier usage)Supprimer les index jamais utilisés : ils ralentissent les écritures sans bénéfice.
Journal des requêtes lentes (profileur)
Section intitulée « Journal des requêtes lentes (profileur) »db.setProfilingLevel(1, { slowms: 100 }); // enregistre les requêtes de plus de 100 msdb.getProfilingStatus();db.system.profile.find().sort({ ts: -1 }).limit(5);db.system.profile .find({ millis: { $gt: 200 } }) .sort({ millis: -1 }) .limit(5);db.setProfilingLevel(0); // désactiveLe profileur écrit dans la collection system.profile de la base : le désactiver une fois l’analyse terminée.
Voir ce qui s’exécute
Section intitulée « Voir ce qui s’exécute »db.currentOp({ active: true, secs_running: { $gt: 5 } }); // opérations actives depuis plus de 5 sdb.killOp(12345); // interrompt l'opération 12345db.serverStatus().connections;db.serverStatus().opcounters;Tailles et statistiques
Section intitulée « Tailles et statistiques »db.stats();db.commandes.stats().size; // taille des donnéesdb.commandes.stats().storageSize; // espace occupé sur le disquedb.commandes.stats().indexSizes;db.commandes.countDocuments();Outils en ligne de commande
Section intitulée « Outils en ligne de commande »mongostat --host 127.0.0.1 5 # activité du serveur toutes les 5 secondes (insertions, requêtes, connexions)mongotop 5 # temps passé en lecture et écriture par collectionConfiguration de la mémoire (WiredTiger)
Section intitulée « Configuration de la mémoire (WiredTiger) »MongoDB utilise par défaut environ la moitié de la mémoire disponible (moins 1 Go) pour son cache. Pour le limiter, dans /etc/mongod.conf :
storage: wiredTiger: engineConfig: cacheSizeGB: 2Redémarrer ensuite : sudo systemctl restart mongod.
Réflexes pour une requête lente
Section intitulée « Réflexes pour une requête lente »explain("executionStats"): repérerCOLLSCANet un écart important entretotalDocsExaminedetnReturned.- Ajouter l’index sur les champs de filtre, de tri et de jointure (
$lookup), en respectant la règle ESR. - Placer
$matchet$sortau début des agrégations, pour utiliser les index. - Limiter les champs renvoyés (projection) et paginer (
limit). - Éviter les documents ou tableaux qui grossissent sans fin.
- Surveiller avec le profileur et
mongostat.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Manuel : Indexes et Analyze Query Performance
- Sauvegarde : Sauvegarde et restauration