Les Index SQL : Accélérer vos Requêtes

CREATE INDEX, types d’index, EXPLAIN, quand indexer, quand ne pas indexer

9
Sections
20+
Exemples
Perf
Optimisation

SECTION 01

C’est quoi un index ?

📖 L’index d’un livre, pour ta base de données

Un index SQL fonctionne comme l’index d’un livre. Au lieu de lire toutes les pages (scan complet de la table), tu regardes l’index pour trouver directement la bonne page (la bonne ligne).

— SANS index : la base parcourt TOUTES les lignes (full table scan)
SELECT * FROM clients WHERE email = ‘alice@email.com’;
— 1 000 000 lignes parcourues → lent

— AVEC index sur email : la base va directement à la bonne ligne
— 3-4 lectures dans l’arbre B-Tree → rapide

Un index accélère les lectures (SELECT, WHERE, JOIN, ORDER BY) mais ralentit les écritures (INSERT, UPDATE, DELETE) car l’index doit être mis à jour à chaque modification. C’est un compromis lecture/écriture.

SECTION 02

Créer et supprimer un index

— Créer un index simple
CREATE INDEX idx_clients_email ON clients(email);

— Créer un index unique (interdit les doublons)
CREATE UNIQUE INDEX idx_clients_email_unique ON clients(email);

— Index composite (plusieurs colonnes)
CREATE INDEX idx_ventes_date_vendeur ON ventes(date, vendeur);

— Voir les index d’une table
SHOW INDEX FROM clients; — MySQL
— \d clients — PostgreSQL

— Supprimer un index
DROP INDEX idx_clients_email ON clients; — MySQL
DROP INDEX idx_clients_email; — PostgreSQL

Convention de nommage : idx_table_colonne (ex : idx_clients_email). Pour les index uniques : uq_table_colonne. Un bon nommage facilite la maintenance.

SECTION 03

Types d’index

TypeStructureUsageSGBD
B-TreeArbre équilibréPar défaut — =, <, >, BETWEEN, ORDER BY, LIKE ‘abc%’Tous
HashTable de hachageÉgalité stricte (=) uniquement, très rapidePostgreSQL, MySQL (Memory)
GINGeneralized InvertedRecherche full-text, arrays, JSONPostgreSQL
GiSTGeneralized Search TreeGéométrie, géolocalisation, rangesPostgreSQL
FULLTEXTIndex de motsRecherche de texte libre (MATCH AGAINST)MySQL, PostgreSQL
SPATIALR-TreeDonnées géographiquesMySQL, PostgreSQL
— B-Tree (par défaut, pas besoin de le spécifier)
CREATE INDEX idx_nom ON clients(nom);

— Hash (PostgreSQL)
CREATE INDEX idx_email_hash ON clients USING hash(email);

— FULLTEXT (MySQL)
CREATE FULLTEXT INDEX idx_bio_ft ON clients(bio);
SELECT * FROM clients WHERE MATCH(bio) AGAINST(‘développeur javascript’);

— GIN pour JSON (PostgreSQL)
CREATE INDEX idx_metadata ON produits USING gin(metadata);

Dans 90% des cas, le B-Tree par défaut suffit. N’utilise les index spécialisés que si tu as un besoin spécifique (recherche full-text, données géo, requêtes JSON).

SECTION 04

Index composites (multi-colonnes)

📚 L’ordre des colonnes est crucial
— Index composite sur (categorie, date)
CREATE INDEX idx_ventes_cat_date ON ventes(categorie, date);

— ✅ Cet index est utilisé pour :
WHERE categorie = ‘Tech’; — colonne 1
WHERE categorie = ‘Tech’ AND date > ‘2025-01-01’; — colonnes 1+2

— ❌ Cet index n’est PAS utilisé pour :
WHERE date > ‘2025-01-01’; — colonne 2 seule

Règle du préfixe gauche : un index composite (A, B, C) est utilisable pour les requêtes sur A, sur A+B, ou sur A+B+C. Mais pas pour B seul ni C seul. Place la colonne la plus filtrée en premier.

🏗️ Covering index
— Si l’index contient TOUTES les colonnes du SELECT,
— la base n’a même pas besoin de lire la table → « index-only scan »

CREATE INDEX idx_ventes_cover ON ventes(categorie, montant);

— Cette requête est entièrement résolue par l’index
SELECT categorie, SUM(montant)
FROM ventes
GROUP BY categorie;

SECTION 05

Quand indexer

SituationIndex recommandé
Colonnes dans le WHERE✅ Oui — surtout si la table a > 1 000 lignes
Colonnes dans le JOIN ON✅ Oui — les FK devraient toujours être indexées
Colonnes dans le ORDER BY✅ Oui — évite un tri en mémoire
Colonnes dans le GROUP BY✅ Oui — accélère le regroupement
PRIMARY KEYAutomatique — index créé par le SGBD
UNIQUEAutomatique — index unique créé par le SGBD
FOREIGN KEY⚠️ Pas automatique en MySQL — à créer manuellement

⚠️ En MySQL, les FOREIGN KEY ne créent pas d’index automatiquement sur la colonne référençante (sauf si un index existe déjà). Crée toujours un index sur les colonnes FK. PostgreSQL et SQL Server les créent automatiquement dans certains cas.

SECTION 06

EXPLAIN — analyser les requêtes

🔍 Vérifier si ton index est utilisé
— MySQL
EXPLAIN SELECT * FROM clients WHERE email = ‘alice@email.com’;

— Résultat (colonnes clés) :
— type | possible_keys | key | rows
— ref | idx_clients_email | idx_clients_email | 1

— PostgreSQL
EXPLAIN ANALYZE SELECT * FROM clients WHERE email = ‘alice@email.com’;
— Index Scan using idx_clients_email on clients
— Execution Time: 0.042 ms

Type (MySQL EXPLAIN)SignificationPerformance
constClé primaire ou unique, 1 ligne🟢 Excellent
eq_refJOIN sur index unique🟢 Excellent
refIndex non unique🟢 Bon
rangePlage de valeurs (BETWEEN, <, >)🟡 Correct
indexScan complet de l’index🟠 Moyen
ALLFull table scan (pas d’index)🔴 Mauvais

Si tu vois type: ALL sur une grosse table, c’est un signal d’alarme. La requête parcourt toutes les lignes. Ajoute un index sur la ou les colonnes du WHERE/JOIN.

SECTION 07

Quand NE PAS indexer

SituationPourquoi pas d’index
Petites tables (< 1 000 lignes)Full scan est aussi rapide qu’un index
Colonnes avec très peu de valeurs distinctesEx : booléen (TRUE/FALSE) — index peu sélectif
Tables avec beaucoup d’INSERT/UPDATEChaque index ralentit les écritures
Colonnes rarement filtréesIndex inutile = espace disque gaspillé
Expressions dans WHEREWHERE YEAR(date) = 2025 — l’index sur date n’est pas utilisé

Un index sur une colonne booléenne (TRUE/FALSE) est quasiment inutile — il ne filtre que 50% des lignes. L’optimiseur préfère un full scan. Exception : si la distribution est très déséquilibrée (ex : 99% FALSE, 1% TRUE), un index peut aider pour chercher les TRUE.

SECTION 08

Erreurs fréquentes

ErreurProblèmeSolution
Fonction sur colonne indexéeWHERE UPPER(nom) = ‘ALICE’ ignore l’indexIndex fonctionnel ou stocker en minuscule
LIKE ‘%alice%’Le % au début empêche l’index B-TreeUtiliser un index FULLTEXT
Trop d’index sur une tableINSERT/UPDATE très lentsMax 5-8 index par table en règle générale
Index composite dans le mauvais ordreIndex pas utilisé (règle du préfixe gauche)Colonne la plus filtrée en premier
Index sur FK oubliéJOIN très lentToujours indexer les colonnes FOREIGN KEY
Index jamais vérifiéIndex inutilisé qui gaspille de l’espaceVérifier avec EXPLAIN, supprimer les inutiles

SECTION 09

Questions fréquentes

Combien d’index maximum par table ?
Il n’y a pas de limite stricte (MySQL supporte jusqu’à 64), mais en pratique, 5 à 8 index par table est un bon équilibre. Chaque index supplémentaire ralentit les INSERT/UPDATE et consomme de l’espace disque. Concentre-toi sur les requêtes les plus fréquentes.
PRIMARY KEY crée-t-il un index ?
Oui, automatiquement. La PRIMARY KEY est un index unique clustered (en MySQL InnoDB, la table est physiquement ordonnée par la PK). Les contraintes UNIQUE créent aussi un index automatiquement. Les FOREIGN KEY, en revanche, ne créent pas toujours d’index.
Un index accélère-t-il COUNT(*) ?
Pas directement. COUNT(*) sans WHERE doit compter toutes les lignes. L’optimiseur peut utiliser le plus petit index disponible pour le scan, mais il doit quand même parcourir toutes les entrées. Un COUNT(*) WHERE colonne_indexée = valeur est en revanche très rapide grâce à l’index.
Index fonctionnel — c’est quoi ?
Un index sur une expression, pas sur une colonne brute : CREATE INDEX idx_lower_email ON clients(LOWER(email)). Permet d’utiliser l’index pour WHERE LOWER(email) = ‘alice@email.com’. Supporté par PostgreSQL nativement et MySQL 8.0.13+.

Les index SQL — Accélérer vos requêtes

Référence : sql.sh Index