Black Friday, 10 h 12 : le site e-commerce affiche un spinner. Le processeur MySQL monte à 98 %, PHP-FPM attend. Dernière release : il y a cinq jours. L'équipe commande un upgrade VPS. À 11 h, un développeur lance EXPLAIN sur la requête checkout — type: ALL, rows: 2 400 000. Index manquant sur (status, created_at) depuis l'ajout du filtre « commandes en attente ».
Un index manquant ne crie pas. Il accumule jusqu'au jour où le volume ou le trafic le rend audible. La bonne nouvelle : la signature est lisible dans le slow log et EXPLAIN — si vous savez regarder avant d'acheter du matériel.
Signatures d'une requête toxique
Dans EXPLAIN, type: ALL signale un parcours complet de table. Un nombre rows énorme par rapport au résultat indique une mauvaise selectivité ou un index absent. Using filesort et Using temporary coûtent cher sur ORDER BY et GROUP BY. Des verrouillages répétés signalent une contention secondaire.
Activez le slow query log :
slow_query_log = 1
long_query_time = 1
log_queries_not_using_indexes = 1
Quand rows_examined / rows_sent > 1000 sur une route HTTP, vous tenez un candidat prioritaire.
De la requête lente à l'index utile
Exemple simplifié :
SELECT * FROM orders
WHERE status = 'pending' AND created_at > '2026-01-01'
ORDER BY created_at DESC LIMIT 50;
Sans index : scan de millions de lignes. Index composite adapté :
CREATE INDEX idx_orders_status_created ON orders (status, created_at);
Ordre des colonnes : égalités d'abord (status =), plages ensuite (created_at >), ORDER BY compatible si possible pour un scan couvrant.
Outils : slow log avec mysqldumpslow, Performance Schema, APM corrélant route et requête. Reproduisez avec données proches de la production — un staging vide ment.
Quand ne pas indexer
Sur une table de quelques milliers de lignes, le scan reste rapide. Une colonne quasi unique déjà clé primaire n'a pas besoin d'un doublon. Sur écritures massives, chaque index coûte. Une requête admin rare préfère cache ou dénormalisation.
Sur mutualisé, le slow log est parfois inaccessible — symptôme « limite processeur atteinte » sans détail. Sur VPS, ajustez innodb_buffer_pool_size après les index, pas avant.
Le sommet : le matériel masque la requête
Avant tout upgrade hébergeur pour « site lent », exigez les trois requêtes les plus lentes des sept derniers jours. Souvent un CREATE INDEX de dix minutes vaut un VPS cent euros. Voir Pool connexions et EXPLAIN PostgreSQL pour l'équivalent Postgres.
Décider et avancer sans angle mort
Activez le slow log en production avec un seuil d'une seconde et collectez une semaine de données avant toute décision matérielle. Lancez EXPLAIN sur les cinq requêtes des routes critiques — checkout, recherche, admin. Créez un index composite, mesurez avant et après sur charge réaliste. Planifiez une revue trimestrielle après gros import de données. Configurez une alerte sur rows_examined via APM pour détecter la dérive avant le prochain pic commercial.
Questions fréquentes
Comment repérer un index manquant ?
Activez le slow query log et triez par lignes examinées. EXPLAIN affiche type ALL ou un index peu sélectif — temps de requête élevé avec rows_examined très supérieur à rows_sent.
Faut-il indexer toutes les colonnes WHERE ?
Non. Créez des index composites dans l'ordre des filtres les plus sélectifs. Trop d'index ralentit INSERT et UPDATE.
Un index peut-il devenir inutile ?
Oui — croissance de table, changement de requêtes ou index redondant via préfixe gauche. Révisez après gros import ou refonte recherche.
Augmenter RAM suffit-il ?
Temporairement. Un parcours complet grandit avec les données — le buffer pool cache le scan une fois, pas la cause structurelle.
Le site ne tombe pas sans raison — il tombe souvent sur une requête qu'EXPLAIN peut accuser en une ligne.