Index de base de données Ils accélèrent les requêtes, mais peuvent ralentir considérablement les opérations d'écriture, consommer beaucoup de mémoire et entraîner l'optimiseur à élaborer des plans défavorables. Je montre concrètement quand les index basculent, comment surviennent les pièges typiques de l'indexation mysql et comment je maintiens un équilibre entre les performances de la base de données et l'optimisation de l'hébergement.
Points centraux
Les points suivants classent les principaux risques et mesures.
- charge d'écriture: chaque index supplémentaire augmente les coûts pour INSERT/UPDATE/DELETE.
- suravalorisations: un nombre trop élevé d'index encombre la mémoire et complique les décisions de l'optimiseur.
- cardinalité: les index sur les colonnes à faible cardinalité apportent peu d'avantages, mais beaucoup de surcharge.
- Ordre: les index composites ne fonctionnent correctement qu'avec un ordre de colonnes approprié.
- Suivi: Mesurer, évaluer, supprimer les index inutilisés – en continu.
Pourquoi les index ralentissent-ils au lieu d'accélérer ?
Je considère les index comme compromis: vous gagnez du temps de lecture, mais cela demande du travail à chaque modification des données. Dans le cas de charges de travail intensives en écriture, cette surcharge s'accumule rapidement, car le moteur doit gérer les arborescences d'index. De nombreux développeurs sous-estiment cela jusqu'à ce que les latences augmentent et que des délais d'attente surviennent. De plus, un trop grand nombre d'options conduit l'optimiseur à choisir des plans sous-optimaux, ce qui est un point de départ classique pour les pièges de l'indexation mysql. Si vous voulez vraiment contrôler les performances de votre base de données, évaluez objectivement les avantages et le coût de chaque index.
Opérations d'écriture : le véritable goulot d'étranglement
Chaque index génère un supplément Overhead avec INSERT, UPDATE et DELETE. J'ai vu des chargements en masse qui s'effectuent en 10 à 15 secondes sans index, mais qui prennent près de deux minutes avec plusieurs index. Cette différence réduit le débit dans les systèmes de journaux et d'événements, dans les paiements en ligne et lors d'importations en masse. Ceux qui chargent des données pendant la nuit désactivent souvent les index secondaires, importent les données, puis les reconstruisent de manière sélective. Cette pratique permet de gagner du temps, à condition de savoir exactement quels index seront réellement utilisés par la suite.
Surindexation et charge mémoire
Les besoins en mémoire sont souvent invisibles jusqu'à ce que le pool de tampons devienne trop petit et IOPS augmenter considérablement. Les colonnes de chaînes augmentent considérablement la taille de l'index, car les informations de longueur et les clés doivent être stockées. Résultat : plus de lectures de pages, plus de pression sur le cache et, au final, plus de latence. Je vérifie donc régulièrement quels index sont réellement utilisés par les requêtes et lesquels ne semblent utiles qu'en théorie. Si vous souhaitez approfondir le sujet, vous trouverez dans mon guide Optimiser la base de données SQL Mesures pratiques pour des structures allégées.
Index incorrects : cardinalité faible et filtres rares
Un index sur une colonne avec cardinalité 2 comme status = {actif, inactif} n'apporte pas grand-chose. Au final, le moteur lit tout de même beaucoup de pages, les mises à jour deviennent plus coûteuses et il n'y a pas de gains réels. Il en va de même pour les colonnes qui n'apparaissent jamais dans WHERE, JOIN ou ORDER BY. Je vois souvent des attributs indexés „ par sécurité “ qui n'accélèrent jamais une requête. Mieux vaut indexer de manière ciblée uniquement là où les filtres sont réels et fréquents.
Indices composites : l'ordre est déterminant
Dans le cas des index à plusieurs colonnes, la Ordre L'efficacité. Un index (col1, col2) n'est utile que si les requêtes filtrent col1 ; les filtres purs sur col2 l'ignorent. Cela crée de fausses attentes, même si le plan semble logique. De plus, il arrive souvent qu'un index unique sur A reste à côté d'un composite (A, B) – ce qui est redondant, car le composite couvre l'index unique. Je supprime systématiquement ces doublons afin de réduire les coûts.
Index clusterisé et clé primaire : largeur, localisation, coûts
InnoDB stocke physiquement les données selon le principe Clé primaire (Index clusterisé). Ce choix influence plusieurs facteurs de coût : localisation d'écriture, fragmentation et taille de tous les index secondaires. En effet, chaque page feuille d'index secondaire contient la clé primaire comme référence à la ligne. Une clé primaire large, riche en texte ou composée se multiplie ainsi dans chaque index – la mémoire ralentit les performances. Je préfère donc une clé de substitution étroite et monotone (BIGINT) plutôt qu'une clé naturelle et large. Cela rend les index secondaires plus compacts, réduit les divisions de pages et améliore les taux de réussite du cache.
UUID vs AUTO_INCREMENT : maîtrise de la localisation des insertions
Les clés aléatoires telles que les UUIDv4 classiques répartissent les insertions sur l'ensemble de l'arbre B. Il en résulte des divisions de pages fréquentes, moins d'écritures cohérentes et une plus grande instabilité de latence. Avec des taux d'écriture élevés, cela peut rapidement devenir problématique. Si vous avez besoin d'UUID, il est préférable d'utiliser triables par date Variantes (par exemple, séquences monotones, UUIDv7/ULID) et les stocke de manière compacte sous forme BINARY(16). Dans de nombreux cas, une clé AUTO_INCREMENT associée à une clé métier unique supplémentaire constitue le choix le plus robuste : les insertions se retrouvent à la fin, les occurrences dans le tampon de modification augmentent et la réplication reste stable.
Optimiseur de requêtes : pourquoi trop d'options sont néfastes
Un nombre trop élevé d'index augmente la zone de recherche de l'optimiseur. Chaque requête doit déterminer s'il est plus avantageux d'utiliser un index ou d'effectuer un balayage complet de la table. Dans certains cas, des statistiques erronées peuvent transformer le plan en une stratégie coûteuse. Je veille donc à ce que la quantité d'index soit réduite et à ce que les statistiques soient à jour afin que les modèles de coûts soient adaptés. Une liberté de choix réduite conduit souvent à des durées d'exécution plus stables.
ORDER BY, LIMIT et Filesort : rendre le tri indexable
De nombreuses requêtes échouent au niveau du tri : ORDER BY + LIMIT semble inoffensif, mais déclenche des tris de fichiers coûteux. Je construis des index de manière à ce que Filtrage et tri correspondance : (user_id, created_at DESC) accélère „ Derniers N événements par utilisateur “ sans étape de tri supplémentaire. MySQL 8.0 prend en charge les index décroissants, ce qui est important lorsque les horodatages sont principalement décroissants. Plus le tri est couvert par l'index, moins l'exécuteur a de travail à faire.
Index fonctionnels et préfixés : une utilisation correcte
Les fonctions sur les colonnes rendent les index inefficaces. C'est pourquoi j'utilise dans MySQL 8.0 index fonctionnels ou colonnes générées: au lieu de WHERE LOWER(email) = ?, j'indexe la forme normalisée – stable et prévisible. Pour les VARCHAR très longs, les Index préfixés (par exemple (hash, title(32))), mais uniquement si la longueur du préfixe offre une sélectivité suffisante. Je vérifie les collisions dans des échantillons avant de me fier aux préfixes.
JOIN, fonctions et index inutilisés
Les JOIN ont besoin d'index sur les Clés des deux côtés, mais trop d'index sur les mêmes colonnes ralentissent considérablement les mises à jour. Les fonctions telles que UPPER(col) ou CAST sur les colonnes indexées désactivent l'index et forcent les analyses. Je remplace ces constructions par des colonnes normalisées ou persistantes supplémentaires, que j'indexe de manière judicieuse. Les jointures à faible cardinalité ralentissent également le système, car trop de lignes partagent les mêmes clés. Je vérifie les requêtes avec EXPLAIN afin de voir leur utilisation réelle.
Partitionnement : élagage oui, surcharge non
Le partitionnement peut réduire les analyses si les Colonne de partitionnement correspond aux filtres les plus fréquents. Chaque partition possède ses propres index – un nombre trop élevé de partitions trop petites augmente la charge administrative et les coûts liés aux métadonnées. Je veille à ce que le partition pruning soit efficace et à ce que le nombre de partitions concernées ne soit pas plus élevé que nécessaire. Pour les séries chronologiques, les partitions périodiques qui peuvent être supprimées par rotation ont fait leurs preuves ; je veille néanmoins à ce que l'environnement d'indexation reste léger pour chaque partition.
Verrouillage, blocages et sélection d'index
Sous REPEATABLE READ, InnoDB verrouille Zones Next Key. Les filtres de plage larges sans index approprié augmentent les plages bloquées, augmentent la probabilité de conflits et provoquent des blocages. Un index précis qui correspond exactement à la clause WHERE réduit les plages bloquées et stabilise les transactions. L'ordre des accès en écriture et la cohérence des plans de requête dans les transactions concurrentes jouent également un rôle : des index moins nombreux et plus adaptés sont utiles, car ils rendent le modèle de recherche plus déterministe.
Fragmentation, maintenance et optimisation de l'hébergement
Augmenter plusieurs indices Entretien Visible : ANALYZE/OPTIMIZE fonctionnent plus longtemps, les reconstructions bloquent les ressources. Sur les hôtes partagés ou multi-locataires, cela a un impact direct sur le CPU et les E/S. Je planifie délibérément les fenêtres de maintenance et réduis le nombre d'index avant les opérations importantes. Mesurer d'abord, agir ensuite : c'est ainsi que j'évite que la maintenance ne devienne elle-même une charge. Je décris d'autres idées de réglage dans „Optimiser les performances MySQL“ en mettant l'accent sur les paramètres liés au cache et à la mémoire.
DDL en ligne et stratégies de déploiement
Les changements d'index dans l'entreprise nécessitent déploiements propres. J'utilise ALGORITHM=INSTANT/INPLACE dans la mesure du possible afin de minimiser les verrous ; les versions plus anciennes ont tendance à revenir à COPY. Les reconstructions d'index sont gourmandes en E/S et augmentent considérablement le trafic de redo/undo. Je limite donc cette action, je la planifie en dehors des heures de pointe ou je construis d'abord l'index sur une réplique, puis je bascule. Important : modifications du schéma par petites étapes, surveillance des latences et chemin de rollback clair.
Réplication et coûts d'indexation
Chaque index supplémentaire augmente non seulement le coût du serveur principal, mais aussi répliques: Le thread SQL applique les mêmes écritures et paie le même prix. En cas de backfills ou de constructions d'index volumineux, les répliques peuvent prendre un retard considérable. Je planifie donc les travaux d'indexation en priorité pour les répliques, je vérifie le retard et je réserve des capacités de mémoire tampon (IOPS, CPU). Si vous effectuez des backfills basés sur le journal binlog, respectez l'ordre suivant : modifiez d'abord les données, puis ajoutez les index, ou inversement, en fonction de la charge de travail.
Statistiques, histogrammes et stabilité du plan
L'optimiseur dépend entièrement de Statistiques. Je mets régulièrement à jour les statistiques (ANALYZE) et j'utilise des histogrammes en cas de distributions asymétriques afin que les sélectivités soient plus réalistes, en particulier sur les colonnes non indexées mais filtrées. Je réduis les fluctuations en supprimant les options redondantes et en augmentant délibérément la cardinalité (par exemple, par une normalisation plus fine au lieu de champs collectifs). L'objectif est d'obtenir un cadre de coûts robuste et reproductible.
Chiffres des tests et tableau : ce qui se passe réellement
Concret Valeurs mesurées illustrent clairement ce compromis. Une insertion en masse d'un million de lignes peut être effectuée en 10 à 15 secondes sans index ; avec de nombreux index secondaires, cela prend près de deux minutes. Les requêtes SELECT bénéficient d'index intelligents, mais atteignent rapidement un plateau à partir duquel les index supplémentaires n'apportent plus grand-chose. Résultat net : la latence de lecture ne diminue que marginalement, tandis que le débit d'écriture chute fortement. Le tableau suivant résume les observations typiques.
| Scénario | SELECT p95 | INSERT Débit | Mémoire indexée | Temps de maintenance/jour |
|---|---|---|---|---|
| Sans index secondaires | ~250 ms | ~60 000 lignes/s | ~0 Go | ~1 à 2 min |
| 5 indices ciblés | ~15 ms | ~25 000 lignes/s | ~1,5 Go | ~6–8 min |
| 12 Index (surindexation) | ~12 ms | ~8 000 lignes/s | ~5,2 Go | ~25–30 min |
Ces chiffres varient en fonction de la répartition des données, du matériel et du profil de requête. La tendance reste toutefois stable : un nombre plus important d'index réduit considérablement les insertions, tandis que le gain en lecture s'amenuise. Je prends donc mes décisions en fonction des données et supprime tout ce qui n'a pas d'effet clair. Je maîtrise ainsi les latences et garde l'esprit et le budget libres.
Utiliser les indices de couverture de manière ciblée
A Couverture Un index contenant toutes les colonnes nécessaires permet d'économiser des pages de table et de réduire les E/S. Exemple : SELECT first_name, last_name WHERE customer_id = ? bénéficie de (customer_id, first_name, last_name). Dans ce cas, l'index agit comme un cache de données au niveau des colonnes. Dans le même temps, je supprime l'index unique sur customer_id s'il est devenu redondant. Moins de structures, même vitesse : cela réduit la maintenance et le stockage.
Surveillance et configuration : mesures pragmatiques
Je commence avec EXPLAIN et EXPLAIN ANALYZE (MySQL 8.0+) et observez les journaux des requêtes lentes. SHOW INDEX FROM table_name révèle les structures inutilisées ou redondantes. J'ajuste ensuite innodb_buffer_pool_size, la taille des fichiers journaux et les stratégies de vidage afin que les index restent en mémoire. Les outils pour les métriques de séries chronologiques aident à garder un œil sur le CPU, les IOPS et les latences. Pour les charges élevées, ce guide est utile : Optimisation de la base de données en cas de charge élevée.
En bref
J'utilise les index de manière consciente et parcimonieuse, car Balance Ce qui compte : la vitesse de lecture, oui, mais pas à n'importe quel prix. Je supprime les colonnes à faible cardinalité, les filtres rares et les index composites mal triés. Chaque structure doit prouver son utilité, sinon elle est supprimée. Les mesures avant et après les modifications permettent d'éviter les décisions instinctives et les mauvais investissements. En hiérarchisant clairement les performances de la base de données et l'optimisation de l'hébergement, vous évitez les pièges de l'indexation mysql et maintenez la latence, le débit et les coûts à un niveau équilibré.


