Introduction
L'optimisation des performances de MongoDB commence souvent par une plainte vague : les requêtes sont lentes, la base de données est à la traîne ou le processeur présente des pics à des heures inhabituelles. Ce guide transforme cette plainte en un flux de travail de diagnostic reproductible. Vous apprendrez à inventorier votre environnement, à profiler les requêtes lentes, à concevoir de meilleurs index, à ajuster les paramètres du serveur MongoDB en toute sécurité et à construire une pile de surveillance qui détecte les régressions avant que les utilisateurs ne les remarquent.
Nous nous concentrons sur des commandes pratiques, adaptées à la version, et des exemples réalistes. Chaque étape comprend la commande, le résultat attendu et un plan de retour en arrière. Ce n'est pas un aperçu théorique ; c'est un manuel de terrain pour les développeurs, les ingénieurs DevOps et les équipes techniques de startups qui exécutent MongoDB en production.
Inventaire de la version et de l'environnement
Avant de toucher à un paramètre, sachez exactement ce que vous exécutez. Cette étape d'inventaire évite les incompatibilités de version et les topologies qui se comportent différemment que prévu.
Prérequis
- Accès au shell MongoDB (
mongosh) ou à une connexion de pilote. - Autorisations de lecture sur la base de données et sur la base de données
adminpour l'état du serveur. - Un terminal avec accès réseau à tous les nœuds mongod/mongos.
Observer avant de changer Exécutez les commandes en lecture seule suivantes et capturez la sortie :
// Connectez-vous à votre déploiement (remplacez les espaces réservés)
mongosh "mongodb://<user>:<password>@<host>:<port>/admin"
// Version du serveur et informations de build
db.version()
// Sortie attendue (exemple) : '7.0.14'
// Topologie du déploiement : autonome, ensemble de réplicas ou cluster fragmenté
db.hello()
// Recherchez les champs 'isWritablePrimary', 'setName', 'hosts'.
// Vérifier les connexions actuelles et le nombre d'opérations
db.serverStatus().connections
// Exemple de sortie : { "current" : 12, "available" : 51188, "totalCreated" : 3421 }
db.serverStatus().opcounters
// Exemple de sortie : { "insert" : 10, "query" : 2500, "update" : 300, "delete" : 45, ... }
Signal attendu vs échec
- Si
db.hello()afficheisWritablePrimary: true, le nœud agit comme primaire. Dans un ensemble de réplicas, confirmez que les autres membres sont dans l'étatSECONDARYviadb.hello()sur chacun. - Si
connections.currentest proche ou égal àconnections.available, vous manquez de capacité de connexion, ce qui entraîne des connexions refusées. - Si
opcounters.queryest considérablement plus élevé que les autres opérations sur la même période, une optimisation de la charge de travail à forte lecture est justifiée.
Rayon d'impact et récupération Cette étape est en lecture seule, donc aucune récupération n'est nécessaire. Cependant, assurez-vous d'être connecté au bon nœud. Si vous vous connectez accidentellement à un primaire de production et exécutez un serverStatus() lourd de manière répétée, vous ajoutez une charge mineure. Utilisez une préférence de lecture secondaryPreferred pour l'observation lorsque c'est possible.
Chemin de configuration sûr
Après l'inventaire, ajustez la configuration un paramètre à la fois. Cela réduit les risques et clarifie l'attribution des effets.
Prérequis
- Vous avez identifié un symptôme de performance spécifique (par exemple, latence d'écriture élevée, requêtes lentes).
- Vous disposez d'une mesure de base à partir de votre outil de surveillance ou
mongostat. - Vous savez comment revenir rapidement en arrière sur le paramètre.
Exemple : Activer le profileur pour les requêtes lentes Le profileur de base de données collecte des informations sur les opérations qui dépassent un seuil de temps. Dans MongoDB 5.0+, utilisez db.setProfilingLevel().
// Lister le niveau de profilage actuel
db.getProfilingStatus()
// Sortie attendue : { "was" : 0, "slowms" : 100, "sampleRate" : 1.0 }
// Définir le niveau de profilage 1 (journaliser les opérations lentes) avec un seuil de 50 ms
db.setProfilingLevel(1, { slowms: 50 })
// Sortie attendue : { "was" : 0, "slowms" : 100, "sampleRate" : 1.0, "ok" : 1 }
Vérification Après quelques minutes ou après avoir reproduit l'opération lente, interrogez la collection system.profile sur la base de données :
// Trouver les requêtes lentes de la dernière heure (triées par durée décroissante)
db.system.profile.find({ ts: { $gt: new Date(Date.now() - 3600*1000) } }).sort({ millis: -1 }).limit(5).pretty()
La sortie attendue inclut le prédicat de la requête, le résumé du plan et le champ millis.
Récupération Si le profilage ajoute un surcoût inacceptable (généralement minime), désactivez-le :
db.setProfilingLevel(0)
Vérification et diagnostics
Vous devez maintenant diagnostiquer les problèmes de performance réels à l'aide d'outils en lecture seule. Cette section couvre les commandes de diagnostic essentielles et comment les interpréter.
1. Identifier les requêtes lentes
Si le profilage est activé, system.profile est le premier arrêt. Sinon, exécutez currentOp() pour voir ce qui se passe en ce moment :
// Trouver les opérations actives en cours depuis plus de 100 ms
db.adminCommand({ currentOp: 1, active: true, secs_running: { $gt: 0.1 } })
Recherchez le champ op (query, insert, update, etc.), le ns (espace de noms) et secs_running. Une opération query qui s'exécute longtemps signale souvent un index manquant ou un mauvais plan.
2. Expliquer une requête
Utilisez explain() pour voir comment MongoDB exécute une requête. Exemple pour une requête sur la collection orders :
db.orders.find({ customer_id: 12345, status: "shipped" }).explain("executionStats")
Champs clés :
executionStats.executionTimeMillis: temps total en ms.executionStats.totalDocsExaminedvsexecutionStats.nReturned: sitotalDocsExaminedest beaucoup plus grand quenReturned, la requête analyse trop de documents.queryPlanner.winningPlan.stage: des étapes commeCOLLSCANsignifient une analyse complète de la collection ;IXSCANsignifie qu'un index a été utilisé.
Conclusion diagnostique Si vous voyez COLLSCAN et totalDocsExamined égal à la taille de la collection mais nReturned petit, vous avez besoin d'un index.
3. Vérifier l'utilisation des index
db.orders.getIndexes()
Cela liste les index existants. Pour les diagnostics de performance, vérifiez également serverStatus().metrics.queryExecutor pour les compteurs d'analyse d'index vs d'analyse de collection.
4. Ensemble de travail et cache
db.serverStatus().wiredTiger.cache
Métriques importantes :
bytes currently in the cache: doit être proche de la taille de cache configurée sous charge stable.- Des pics de
pages read into cacheindiquent des échecs de cache et des E/S disque.
Si les échecs de cache sont élevés, envisagez d'augmenter wiredTiger.engineConfig.cacheSizeGB (voir le réglage des paramètres ci-dessous).
Réglage des index et des requêtes
C'est le domaine le plus impactant pour l'optimisation des performances. Une mauvaise indexation est la cause numéro un des requêtes lentes dans MongoDB.
Créer le bon index
Étant donné la requête orders précédente, créez un index composé :
// Index sur customer_id croissant, status décroissant
db.orders.createIndex({ customer_id: 1, status: -1 }, { name: "idx_customer_status" })
Vérification Réexécutez explain("executionStats") sur la requête. Attendu : winningPlan.stage devient IXSCAN, totalDocsExamined chute considérablement et executionTimeMillis diminue.
Sélectivité et ordre des index
L'ordre compte dans les index composés. Pour les requêtes qui filtrent sur customer_id et status, le champ le plus fréquemment utilisé seul ou ayant une forte sélectivité doit venir en premier. Utilisez db.orders.aggregate([{ $match: { customer_id: 12345, status: "shipped" } }, { $group: { _id: "$status", count: { $sum: 1 } } }]) pour évaluer la sélectivité.
Éviter les erreurs courantes d'indexation
- Sur-indexation : chaque index ajoute une surcharge d'écriture et consomme de la RAM. Ne créez que des index qui soutiennent les modèles de requêtes réels.
- Indexer des champs à faible cardinalité : indexer
statusseul (avec peu de valeurs distinctes) entraîne souvent une faible sélectivité et peut ne pas être utilisé. - Ignorer les étapes de tri : si une requête trie par
created_at, incluez ce champ dans l'index dans le bon ordre de tri pour éviter le tri en mémoire.
Surveiller l'utilisation des index Utilisez $indexStats pour voir la fréquence d'utilisation de chaque index :
db.orders.aggregate([{ $indexStats: {} }])
Recherchez les index avec accesses.ops à 0 ; ce sont des candidats à la suppression (mais confirmez qu'il n'y a pas d'utilisation cachée).
Réglage des paramètres du serveur
Au-delà des index, la configuration du serveur MongoDB peut être ajustée. Changez toujours un paramètre à la fois et surveillez.
Taille du cache WiredTiger
Pour les serveurs MongoDB dédiés, la taille de cache par défaut est de 50 % de (RAM - 1 Go). Cela peut être augmenté sur les systèmes riches en mémoire. Vous pouvez la définir dans le fichier de configuration :
storage:
wiredTiger:
engineConfig:
cacheSizeGB: 4
Sur un serveur en cours d'exécution, utilisez db.adminCommand({ setParameter: 1, wiredTigerEngineRuntimeConfig: "cache_size=4GB" }) mais cela ne persiste pas après un redémarrage. Persistez via le fichier de configuration ou le paramètre de démarrage.
Vérification Vérifiez l'utilisation du cache au fil du temps avec db.serverStatus().wiredTiger.cache. Recherchez une diminution de pages read into cache et une augmentation de bytes currently in the cache jusqu'à la nouvelle limite.
Récupération Si vous observez une augmentation du swapping ou des problèmes de mémoire insuffisante, revenez à la valeur de cache précédente.
Taille de l'oplog (ensembles de réplicas)
L'oplog doit être suffisamment grand pour contenir les opérations entre les cycles de synchronisation des secondaires. Si les secondaires prennent du retard, ils peuvent entrer dans l'état RECOVERING. Vérifiez la taille actuelle de l'oplog :
db.printReplicationInfo()
// Exemple de sortie : configured oplog size: 990MB; log length start to end: 2.5hrs
Si la log length est inférieure à votre fenêtre de maintenance, envisagez un redimensionnement. Changer la taille de l'oplog nécessite un redémarrage roulant avec le paramètre --oplogSize ou replSetResizeOplog (MongoDB 4.4+).
Limites du pool de connexions
Les pilotes gèrent généralement les pools de connexions. Assurez-vous que le maxIncomingConnections du serveur est suffisamment élevé :
db.adminCommand({ getParameter: 1, maxIncomingConnections: 1 })
La valeur par défaut est 65536, mais vous devrez peut-être la réduire ou l'augmenter en fonction du matériel. Dans le fichier de configuration :
net:
maxIncomingConnections: 10000
Surveillance et alertes
La surveillance proactive détecte tôt les régressions de performance. Mettez en place des métriques au niveau de la base de données et au niveau de l'hôte.
Utilisation de mongostat et mongotop
mongostat fournit une vue en temps réel rapide :
mongostat --host <host> --username <user> --password <password> --authenticationDatabase admin
Exemple de colonnes de sortie : insert, query, update, delete, vsize, res, netIn, netOut, conn, time. Surveillez les taux élevés de query ou update et un conn élevé (connexions).
mongotop affiche l'activité de lecture/écriture par collection :
mongotop --host <host> --username <user> --password <password> --authenticationDatabase admin
Cela aide à identifier les collections chaudes.
Intégration avec Prometheus et Grafana
Utilisez l'exportateur mongodb_exporter (Percona ou Bitnami) pour collecter les métriques. Métriques clés sur lesquelles alerter :
mongodb_op_counters_totalpour les taux d'opérations.mongodb_mongod_connectionspour le nombre de connexions.mongodb_mongod_oplog_stats_sizepour l'utilisation de l'oplog.mongodb_mongod_wiredtiger_cache_bytespour l'utilisation du cache.
Exemple de règle d'alerte Prometheus :
groups:
- name: mongodb_alerts
rules:
- alert: MongoDBHighConnections
expr: mongodb_mongod_connections > 5000
for: 10m
labels:
severity: warning
annotations:
summary: "Connexions élevées sur {{ $labels.instance }}"
- alert: MongoDBCacheMissRate
expr: rate(mongodb_mongod_wiredtiger_cache_pages_read_into_cache[5m]) / rate(mongodb_mongod_wiredtiger_cache_pages_requested[5m]) > 0.05
for: 15m
labels:
severity: critical
annotations:
summary: "Taux élevé d'échecs de cache sur {{ $labels.instance }}"
Configurez des alertes pour le nombre de requêtes lentes via le profilage : vous pouvez exporter system.profile sous forme de journaux ou utiliser un expéditeur de journaux.
Pièges courants et comment les éviter
Même les ingénieurs expérimentés tombent dans ces pièges. Voici comment les éviter ou s'en remettre.
Piège 1 : Ajouter des index sans vérification
Pourquoi cela arrive : Un développeur voit une requête lente et ajoute aveuglément un index suggéré par un outil ou un article de blog. L'index peut ne pas correspondre au modèle de requête, ou il peut causer une amplification d'écriture.
Comment éviter : Exécutez toujours explain() avant et après. Utilisez la méthode hint() pour forcer l'utilisation de l'index lors des tests et comparer les performances. Vérifiez $indexStats après quelques jours pour voir si l'index est réellement utilisé.
Récupération : Si un index est inutilisé ou nuisible, supprimez-le avec db.collection.dropIndex("index_name"). Mais d'abord, confirmez qu'aucun code d'application ne l'attend ou ne l'utilise sporadiquement.
Piège 2 : Ignorer la contention d'écriture
Pourquoi cela arrive : Les lectures sont optimisées, mais les écritures deviennent lentes en raison d'un trop grand nombre d'index ou de verrous au niveau des documents. Les utilisateurs se plaignent de la latence des mises à jour.
Comment éviter : Surveillez db.serverStatus().wiredTiger.concurrentTransactions et examinez la disponibilité en écriture. Utilisez moins d'index sur les collections à forte écriture. Envisagez le sharding si le volume d'écriture dépasse la capacité d'un seul nœud.
Récupération : Supprimez les index inutiles et, si nécessaire, repensez le schéma pour éviter les documents volumineux qui provoquent des divisions de pages.
Piège 3 : Définir une taille de cache trop élevée
Pourquoi cela arrive : Un opérateur voit une utilisation élevée du cache et augmente cacheSizeGB à 80 % de la RAM, laissant peu de place au système d'exploitation et provoquant du swapping.
Comment éviter : Suivez les directives de la documentation MongoDB : cacheSizeGB doit être défini à 50 % de (RAM - 1 Go) ou au maximum 80 % de la mémoire disponible pour les serveurs dédiés, mais surveillez les métriques de mémoire de l'hôte. Utilisez db.serverStatus().wiredTiger.cache et free -m sur l'hôte pour vous assurer qu'il n'y a pas de swap.
Récupération : Si vous observez une utilisation du swap (vmstat 1 montre si ou so non nul), réduisez immédiatement cacheSizeGB et redémarrez mongod (ou définissez au moment de l'exécution puis persistez plus tard).
Piège 4 : Ne pas surveiller l'oplog
Pourquoi cela arrive : Une petite startup fonctionne bien, puis après une migration de données importante ou une période de forte écriture, les secondaires prennent du retard et ne peuvent pas rattraper car l'oplog a écrasé les entrées nécessaires.
Comment éviter : Configurez une alerte pour la fenêtre de l'oplog. Utilisez db.printReplicationInfo() pour vérifier la fenêtre. La fenêtre doit être au moins le temps nécessaire pour une resynchronisation complète ou une maintenance.
Récupération : Si un secondaire est trop en retard, effectuez une synchronisation initiale à partir d'une sauvegarde récente ou d'un autre membre. Augmentez proactivement la taille de l'oplog.
Piège 5 : Négliger le réglage du pool de connexions dans l'application
Pourquoi cela arrive : Les développeurs utilisent les tailles de pool par défaut des pilotes, qui peuvent être trop faibles pour une forte concurrence ou trop élevées pour des connexions serveur limitées.
Comment éviter : Effectuez des benchmarks avec une concurrence réaliste. Définissez la taille du pool pour gérer les charges de pointe sans submerger le serveur. Dans Node.js avec le pilote natif :
const { MongoClient } = require('mongodb');
const client = new MongoClient(uri, { maxPoolSize: 100, minPoolSize: 10 });
Récupération : Si vous voyez des délais de connexion fréquents, augmentez la taille du pool ; si les connexions serveur sont épuisées, réduisez la taille du pool ou ajoutez une limite de connexion sur le serveur.
Liste de contrôle des opérations
Utilisez cette liste de contrôle avant et après chaque changement de performance. Assignez un responsable à chaque élément et révisez chaque semaine ou après chaque changement important.
| Élément | Responsable | Fréquence | Vérification |
|---|---|---|---|
| Métriques de base enregistrées (latence, débit, utilisation des ressources) | Responsable DBA | Avant le changement | Capturer la sortie mongostat et le nombre de requêtes lentes pendant 1 heure |
| Modèles de requêtes et couverture des index analysés | Ingénieur backend | Avant le déploiement du code | explain() sur les 10 principales requêtes montre IXSCAN, pas de COLLSCAN |
| Nouveaux index créés en staging et testés | Ingénieur de base de données | Avant la mise en production | Comparaison de performance côte à côte sur un clone |
| Pool de connexions de l'application dimensionné correctement | Ingénieur backend | À chaque version | Test de charge avec la concurrence de pointe attendue |
| Taille du cache et de l'oplog du serveur examinée | Responsable DBA | Mensuel | db.printReplicationInfo() et db.serverStatus().wiredTiger.cache examinés |
| Alertes de surveillance activées pour les métriques clés | Ingénieur DevOps | Mensuel | Tester l'alerte en simulant un dépassement de seuil |
| Plan de retour en arrière documenté pour chaque changement | Responsable DBA | Par changement | Commande de retour ou restauration depuis la sauvegarde vérifiée |
Exemples de responsables : Priya Shah, responsable ingénierie (Responsable DBA) ; Alex Chen, ingénieur backend ; Jordan Lee, ingénieur DevOps.
Conclusion
L'optimisation des performances de MongoDB est un processus itératif : observer, formuler des hypothèses, changer une variable, mesurer, puis soit conserver, soit revenir en arrière. Ce guide vous a donné les outils pour inventorier votre environnement, trouver les requêtes lentes, ajouter des index efficaces, ajuster les paramètres du serveur en toute sécurité et surveiller les régressions.
Maintenant, choisissez une action à faible risque de ce guide, comme activer le profileur avec un seuil de 50 ms, et exécutez-la dans votre environnement de développement. Enregistrez la base, effectuez le changement et comparez les résultats après une journée. La confiance que vous gagnerez grâce à cette petite amélioration vérifiée alimentera vos initiatives de performance plus larges.
Rappelez-vous : l'objectif n'est pas d'appliquer chaque conseil d'optimisation, mais de construire une approche systématique qui maintient votre déploiement MongoDB rapide et stable.