SQL Pratique
Index SQL : création et optimisation pour la performance
15 min de lecture

Index SQL : création et optimisation pour la performance

Maîtrisez les index SQL : types B-tree et clustered, création optimale et techniques d'optimisation. Guide complet avec exemples pratiques.

Avatar de Thomas LeroyThomas Leroy

Les index SQL sont les clés de voûte de la performance des bases de données. Un index bien conçu peut transformer une requête lente de plusieurs secondes en une exécution quasi-instantanée, tandis qu'un mauvais indexage peut dégrader les performances d'écriture et consommer inutilement de l'espace disque.

Dans cet article, nous explorerons les différents types d'index, leurs mécanismes internes, et surtout comment les créer et les optimiser efficacement. Que vous prépariez un entretien technique ou que vous cherchiez à améliorer les performances de vos applications en production, cette connaissance approfondie des index vous sera indispensable.

Les index ne sont pas qu'une simple optimisation : ils constituent un élément fondamental de l'architecture des bases de données modernes. Comprendre leur fonctionnement vous permettra de prendre des décisions éclairées sur votre stratégie d'indexage.

📌 Ce qu'il faut retenir

  • Les index B-tree sont les plus courants et efficaces pour la plupart des cas d'usage
  • Un index clustered organise physiquement les données, contrairement aux index non-clustered
  • Les index composites permettent d'optimiser les requêtes multi-colonnes
  • EXPLAIN PLAN révèle l'utilisation réelle des index par l'optimiseur de requêtes

Les types d'index SQL essentiels

Index B-tree : la référence universelle

L'index B-tree (Balanced Tree) constitue le type d'index le plus répandu dans les systèmes de gestion de bases de données. Sa structure arborescente équilibrée garantit des performances de recherche logarithmiques O(log n), même sur des tables de plusieurs millions d'enregistrements.

Le principe du B-tree repose sur une hiérarchie de nœuds : les nœuds internes contiennent des clés de routage, tandis que les feuilles stockent les pointeurs vers les lignes de données. Cette organisation permet d'effectuer des recherches, des insertions et des suppressions avec une complexité temporelle prévisible.

PostgreSQL, MySQL et SQL Server utilisent les B-tree comme structure d'index par défaut. Oracle Database emploie une variante appelée B+ tree, où seules les feuilles contiennent les données, optimisant ainsi les parcours séquentiels.

Index clustered vs non-clustered

Un index clustered détermine l'ordre physique de stockage des données sur le disque. Chaque table ne peut avoir qu'un seul index clustered, généralement défini sur la clé primaire. Les pages de données sont organisées selon l'ordre de cet index, ce qui optimise considérablement les requêtes de plage.

Les index non-clustered, en revanche, pointent vers les lignes de données sans influencer leur organisation physique. Une table peut avoir plusieurs index non-clustered, chacun optimisant différents types de requêtes.

SQL Server illustre parfaitement cette distinction : l'index clustered sur la clé primaire organise physiquement les données, tandis que les index non-clustered créent des structures séparées pointant vers ces données.

💡 Bon à savoir

Sur une table sans index clustered (heap), les index non-clustered pointent directement vers les lignes via des Row ID. Avec un index clustered, ils pointent vers les clés de l'index clustered, ajoutant une indirection mais garantissant la stabilité des références.

Création d'index : syntaxe et bonnes pratiques

Syntaxe de base CREATE INDEX

La création d'un index suit une syntaxe standardisée across les différents SGBD, avec quelques variations spécifiques :

-- Syntaxe générale
CREATE INDEX idx_nom_colonne ON table_name (colonne);

-- Index composite
CREATE INDEX idx_composite ON commandes (client_id, date_commande);

-- Index unique
CREATE UNIQUE INDEX idx_unique_email ON utilisateurs (email);

-- Index partiel (PostgreSQL)
CREATE INDEX idx_commandes_actives ON commandes (client_id) 
WHERE statut = 'actif';

La convention de nommage des index doit être cohérente et descriptive. Le préfixe "idx_" suivi du nom de la table et des colonnes concernées facilite la maintenance et la compréhension du schéma.

Pour les index composites, l'ordre des colonnes dans la définition est crucial. La règle générale consiste à placer les colonnes les plus sélectives en premier, suivies de celles utilisées dans les clauses ORDER BY.

Index composites et ordre des colonnes

L'efficacité d'un index composite dépend directement de l'ordre de ses colonnes. Un index sur (A, B, C) peut servir les requêtes filtrant sur A, sur (A, B), ou sur (A, B, C), mais pas efficacement sur B seul ou C seul.

Cette propriété découle de la structure B-tree : les entrées sont triées d'abord par la première colonne, puis par la seconde en cas d'égalité, et ainsi de suite. Une requête cherchant uniquement la colonne B doit parcourir toute la structure d'index.

L'ordre optimal place généralement les colonnes les plus discriminantes en premier. Une colonne avec une cardinalité élevée (beaucoup de valeurs distinctes) sera plus sélective qu'une colonne binaire ou avec peu de valeurs distinctes.

-- Bon ordre pour un index composite
CREATE INDEX idx_ventes_optimise ON ventes (
    date_vente,      -- Haute cardinalité, souvent filtrée
    produit_id,      -- Cardinalité moyenne
    statut           -- Faible cardinalité, mais souvent dans WHERE
);

Types d'index avancés selon les SGBD

Index bitmap et fonctionnels

Oracle Database propose des index bitmap particulièrement efficaces pour les colonnes à faible cardinalité. Ces index stockent des bitmaps pour chaque valeur distincte, permettant des opérations booléennes très rapides entre différents prédicats.

Les index fonctionnels (ou basés sur des expressions) permettent d'indexer le résultat d'une fonction plutôt que la valeur brute de la colonne. PostgreSQL et Oracle supportent cette fonctionnalité :

-- Index fonctionnel PostgreSQL
CREATE INDEX idx_email_lower ON utilisateurs (LOWER(email));

-- Index sur expression Oracle
CREATE INDEX idx_total_calcule ON commandes (quantite * prix_unitaire);

Cette approche évite les calculs répétitifs dans les requêtes et permet d'optimiser des prédicats complexes.

Index partiels et conditionnels

PostgreSQL et SQL Server permettent de créer des index partiels qui ne couvrent qu'un sous-ensemble des lignes répondant à une condition WHERE. Cette technique réduit la taille de l'index et améliore les performances pour des requêtes spécifiques.

-- Index partiel sur les commandes actives uniquement
CREATE INDEX idx_commandes_actives 
ON commandes (client_id, date_commande)
WHERE statut IN ('en_cours', 'validée');

L'index partiel est particulièrement utile quand une grande partie des données n'est jamais interrogée (archives, données supprimées logiquement, etc.).

Analyse des performances avec EXPLAIN PLAN

Interpréter les plans d'exécution

EXPLAIN PLAN révèle comment l'optimiseur de requêtes utilise (ou n'utilise pas) vos index. Chaque SGBD a sa syntaxe, mais les concepts restent similaires :

-- PostgreSQL
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM commandes WHERE client_id = 123;

-- SQL Server
SET STATISTICS IO ON;
SELECT * FROM commandes WHERE client_id = 123;

-- Oracle
EXPLAIN PLAN FOR SELECT * FROM commandes WHERE client_id = 123;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

Les métriques clés à surveiller incluent le coût estimé, le nombre de lignes lues, les opérations d'E/S disque, et surtout la présence d'Index Seek versus Table Scan.

Un Index Seek indique que l'optimiseur utilise efficacement votre index pour localiser directement les données pertinentes. Un Table Scan (ou Index Scan complet) suggère que l'index n'est pas optimal pour cette requête.

Identifier les index manquants ou inutilisés

Les SGBD modernes fournissent des vues système pour identifier les index manquants ou sous-utilisés :

-- SQL Server - Index manquants
SELECT 
    migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) AS improvement_measure,
    'CREATE INDEX [missing_index_' + CONVERT(varchar, mig.index_group_handle) + '_' + CONVERT(varchar, mid.index_handle) 
    + '_' + LEFT(PARSENAME(mid.statement, 1), 20) + '] ON ' + mid.statement + ' (' + ISNULL(mid.equality_columns,'') 
    + CASE WHEN mid.equality_columns IS NOT NULL AND mid.inequality_columns IS NOT NULL THEN ',' ELSE '' END 
    + ISNULL(mid.inequality_columns, '') + ')' + ISNULL(' INCLUDE (' + mid.included_columns + ')', '') AS create_index_statement
FROM sys.dm_db_missing_index_groups mig
INNER JOIN sys.dm_db_missing_index_group_stats migs ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle;

PostgreSQL offre l'extension pg_stat_statements pour analyser l'utilisation des index et identifier les requêtes coûteuses qui bénéficieraient d'un indexage supplémentaire.

⚠️ Attention

Les suggestions d'index automatiques doivent être validées manuellement. Un index peut améliorer une requête spécifique tout en dégradant les performances globales d'écriture ou d'autres requêtes.

Optimisation et maintenance des index

Stratégies de maintenance proactive

Les index se fragmentent au fil du temps avec les insertions, modifications et suppressions. Cette fragmentation dégrade les performances de lecture et augmente l'espace disque consommé. Une maintenance régulière est donc essentielle.

SQL Server distingue la fragmentation interne (pages partiellement remplies) de la fragmentation logique (pages non contiguës). Un REORGANIZE suffit pour une fragmentation modérée (5-30%), tandis qu'un REBUILD complet s'impose au-delà de 30%.

-- Analyser la fragmentation SQL Server
SELECT 
    object_name(ips.object_id) AS table_name,
    i.name AS index_name,
    ips.avg_fragmentation_in_percent,
    ips.page_count
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
INNER JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.avg_fragmentation_in_percent > 5;

PostgreSQL utilise VACUUM et REINDEX pour maintenir les index. Oracle recommande de surveiller les statistiques via DBMS_STATS et de reconstruire les index très fragmentés.

Optimisation des requêtes et couverture d'index

Un index de couverture (covering index) inclut toutes les colonnes nécessaires à une requête, éliminant ainsi le besoin d'accéder à la table principale. Cette technique, particulièrement efficace sur SQL Server avec la clause INCLUDE, peut transformer radicalement les performances.

-- Index de couverture SQL Server
CREATE INDEX idx_commandes_covering 
ON commandes (client_id, date_commande)
INCLUDE (montant_total, statut, produit_id);

Les colonnes dans INCLUDE ne participent pas à la structure B-tree mais sont stockées dans les feuilles, évitant les key lookups coûteux.

Pour les requêtes analytiques complexes, considérez les index columnaires (SQL Server, Oracle) qui comprennent et stockent les données par colonne plutôt que par ligne, optimisant ainsi les agrégations sur de gros volumes.

Comparaison des performances par type d'index

Type d'index Cas d'usage optimal Performance SELECT Impact INSERT/UPDATE Espace disque
B-tree simple Recherches exactes, plages Excellent Faible Modéré
B-tree composite Requêtes multi-colonnes Très bon Modéré Plus élevé
Clustered Clé primaire, requêtes de plage Excellent (plages) Variable Aucun (réorganise)
Index partiel Sous-ensemble spécifique Excellent (subset) Faible Réduit
Index fonctionnel Expressions calculées Bon (expressions) Élevé Variable
Bitmap (Oracle) Faible cardinalité Très bon (WHERE) Élevé Compact

Cas d'usage avancés et patterns d'optimisation

Index pour les jointures complexes

L'optimisation des jointures nécessite une stratégie d'indexage coordonnée entre les tables. Pour une jointure equi-join classique, chaque table doit avoir un index sur sa colonne de jointure. L'ordre des tables dans le plan d'exécution dépend de ces index.

-- Optimisation d'une jointure trois tables
CREATE INDEX idx_commandes_client ON commandes (client_id);
CREATE INDEX idx_lignes_commande ON lignes_commande (commande_id);
CREATE INDEX idx_produits_id ON produits (produit_id);

-- La requête bénéficiera des trois index
SELECT c.numero, p.nom, lc.quantite
FROM commandes c
JOIN lignes_commande lc ON c.id = lc.commande_id
JOIN produits p ON lc.produit_id = p.produit_id
WHERE c.client_id = 123;

Pour les jointures sur des plages de valeurs, un index clustered sur la table la plus volumineuse améliore significativement les performances en réduisant les E/S disque.

Patterns pour les données temporelles

Les données temporelles présentent des défis spécifiques d'indexage. Les requêtes portent souvent sur des plages de dates, nécessitant des index optimisés pour ces patterns d'accès.

Le partitionnement par date combiné à des index locaux constitue une approche efficace pour les gros volumes. Chaque partition dispose de ses propres index, réduisant la taille des structures et améliorant les performances de maintenance.

-- Pattern temporel avec index composite
CREATE INDEX idx_events_time_type ON events (created_at, event_type, user_id);

-- Requête optimisée
SELECT user_id, COUNT(*) 
FROM events 
WHERE created_at >= '2026-01-01' 
  AND created_at < '2026-02-01'
  AND event_type = 'purchase'
GROUP BY user_id;

L'ordre des colonnes dans l'index temporel suit généralement : date (pour la sélectivité), type/catégorie (pour le filtrage), puis identifiants (pour les jointures).

Surveillance et métriques de performance

Métriques clés à surveiller

La surveillance proactive des index nécessite de tracker plusieurs métriques essentielles. Le ratio hit de cache d'index indique si vos index les plus utilisés restent en mémoire. Un ratio faible suggère soit un manque de RAM, soit des index trop volumineux.

Le nombre d'utilisations par index révèle les index redondants ou inutilisés. Un index jamais utilisé consomme inutilement des ressources et ralentit les écritures. Inversement, un index très sollicité justifie peut-être des optimisations supplémentaires.

-- Statistiques d'utilisation PostgreSQL
SELECT 
    schemaname,
    tablename,
    indexname,
    idx_tup_read,
    idx_tup_fetch,
    idx_scan
FROM pg_stat_user_indexes 
ORDER BY idx_scan DESC;

Les métriques de fragmentation évoluent différemment selon le pattern d'usage. Les tables avec beaucoup d'INSERT/DELETE présentent une fragmentation plus rapide que les tables principalement en lecture.

Automatisation de la maintenance

L'automatisation de la maintenance d'index prévient la dégradation progressive des performances. SQL Server Agent, PostgreSQL cron jobs, ou Oracle DBMS_SCHEDULER peuvent planifier des tâches de maintenance adaptées à votre charge de travail.

-- Script de maintenance automatique SQL Server
DECLARE @fragmentation FLOAT;
DECLARE @sql NVARCHAR(1000);

SELECT @fragmentation = avg_fragmentation_in_percent 
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('ma_table'), 1, NULL, 'LIMITED');

IF @fragmentation > 30
    SET @sql = 'ALTER INDEX ALL ON ma_table REBUILD';
ELSE IF @fragmentation > 5
    SET @sql = 'ALTER INDEX ALL ON ma_table REORGANIZE';

IF @sql IS NOT NULL
    EXEC sp_executesql @sql;

L'automatisation doit respecter les fenêtres de maintenance et éviter les heures de pointe. Les opérations REBUILD bloquent généralement les écritures, contrairement aux REORGANIZE qui s'exécutent en ligne.

Comme nous l'avons vu dans notre guide sur l'optimisation des requêtes SQL, la stratégie d'indexage fait partie intégrante d'une approche globale de performance.

Questions fréquentes

Combien d'index peut-on créer sur une table ?

Techniquement, la plupart des SGBD permettent de créer des centaines d'index sur une seule table, mais la limite pratique est bien plus basse. Chaque index supplémentaire ralentit les opérations d'écriture (INSERT, UPDATE, DELETE) car le moteur doit maintenir toutes les structures d'index synchronisées.

En pratique, 5 à 10 index par table constituent généralement un maximum raisonnable. Au-delà, les bénéfices en lecture sont souvent annulés par la dégradation des performances d'écriture. L'exception concerne les tables de dimension dans les entrepôts de données, principalement en lecture, qui peuvent supporter plus d'index.

La règle d'or consiste à créer uniquement les index réellement utilisés par vos requêtes critiques, identifiés par l'analyse des plans d'exécution et des statistiques d'usage.

Un index composite peut-il remplacer plusieurs index simples ?

Un index composite sur (A, B, C) peut effectivement optimiser les requêtes filtrant sur A seul, ou sur (A, B), grâce à la propriété de préfixe des B-trees. Il peut donc potentiellement remplacer des index simples sur A et (A, B).

Cependant, cet index composite ne sera pas efficace pour des requêtes filtrant uniquement sur B ou C. Si ces patterns de requêtes sont fréquents, des index simples dédiés restent nécessaires. La décision dépend de l'analyse de votre charge de travail réelle.

L'avantage des index composites réside dans leur efficacité pour les requêtes multi-critères et leur moindre consommation d'espace comparé à plusieurs index simples couvrant les mêmes colonnes.

Comment savoir si un index est utilisé efficacement ?

L'analyse des plans d'exécution avec EXPLAIN PLAN constitue la méthode principale pour vérifier l'utilisation d'un index. Recherchez les opérations "Index Seek" ou "Index Range Scan" qui indiquent une utilisation optimale, par opposition aux "Table Scan" ou "Index Full Scan".

Les statistiques système des SGBD fournissent également des métriques précieuses : nombre d'utilisations, coût moyen des requêtes, ratio pages lues/lignes retournées. Un bon index présente un ratio élevé entre les utilisations et un coût faible par requête.

Surveillez particulièrement le nombre de pages lues versus le nombre de lignes retournées. Un index inefficace génère beaucoup d'E/S disque pour peu de résultats, révélant une mauvaise sélectivité ou un ordre de colonnes non optimal.

Faut-il indexer les clés étrangères ?

Oui, indexer les clés étrangères est généralement une bonne pratique, particulièrement pour les jointures fréquentes. Un index sur une clé étrangère accélère drastiquement les opérations JOIN entre tables liées, transformant des parcours complets en recherches directes.

L'indexage des clés étrangères améliore également les performances des requêtes de validation d'intégrité référentielle lors des suppressions dans la table parent. Sans index, le SGBD doit scanner entièrement la table enfant pour vérifier l'absence de références.

Attention cependant aux clés étrangères à très faible cardinalité (comme un statut binaire). Dans ces cas, l'index peut être moins bénéfique et un index partiel ciblant seulement certaines valeurs peut s'avérer plus efficace.

Quand reconstruire un index fragmenté ?

La décision de reconstruire un index dépend du niveau de fragmentation et du type de SGBD. SQL Server recommande un REORGANIZE entre 5% et 30% de fragmentation, et un REBUILD au-delà de 30%. PostgreSQL utilise plutôt VACUUM pour la maintenance courante et REINDEX pour les cas sévères.

La fragmentation logique (pages non contiguës) impacte plus les performances que la fragmentation interne (pages partiellement remplies). Priorisez la reconstruction des index très sollicités et présentant une fragmentation logique élevée.

Planifiez les opérations de maintenance durant les fenêtres de faible activité, car REBUILD bloque généralement les écritures. Pour les environnements 24/7, préférez les opérations ONLINE quand elles sont disponibles, même si elles consomment plus de ressources temporairement.

Conclusion

La maîtrise des index SQL représente un investissement stratégique pour tout professionnel travaillant avec des bases de données. Les concepts abordés - des B-trees aux index composites, en passant par l'analyse avec EXPLAIN PLAN - constituent les fondamentaux pour optimiser efficacement vos requêtes et applications.

L'indexage optimal résulte d'un équilibre délicat entre performances de lecture et coûts d'écriture et de maintenance. Cette expertise s'acquiert par la pratique et l'analyse continue de vos charges de travail réelles.

Pour approfondir vos compétences en optimisation SQL et vous préparer aux défis techniques des entretiens, notre plateforme SQL Pratique vous propose des exercices progressifs avec des environnements d'exécution réels. Transformez cette théorie en expertise pratique dès maintenant.

Prêt à vous entraîner ?

50 exercices SQL interactifs avec éditeur en ligne, chronomètre et feedback IA.

Voir les exercices