Hasina Razafintsalama

Hasina RAZAFINTSALAMA

← Retour au Blog
Backend

Les index de base de données expliqués

Un index est une structure de recherche triée qui échange des écritures plus lentes et plus de disque contre des lectures bien plus rapides. Voici comment fonctionne un index B-tree, quoi indexer, pourquoi l'ordre des colonnes d'un index composite compte, et quand un index nuit.

2026-09-04·11 min

Un index est une structure de données séparée et triée qui permet à la base de trouver des lignes sans scanner toute la table. C'est la correction de performance la plus efficace pour la plupart des applications, et la plus mal comprise : un index n'est pas gratuit, il ralentit chaque écriture, et le mauvais index ne fait rien. Voici comment ils fonctionnent et comment les choisir, pour MySQL comme pour PostgreSQL.

Comment fonctionne un index B-tree

Pensez à l'index à la fin d'un livre : des termes triés par ordre alphabétique, chacun pointant vers une page. Pour trouver un terme vous ne lisez pas le livre, vous sautez à peu près au bon endroit et vous affinez. Un index B-tree de base de données, c'est pareil : les valeurs indexées gardées triées dans un arbre équilibré, donc une recherche est une poignée d'étapes (O(log n)) au lieu de lire chaque ligne (O(n)). Le compromis, c'est que l'arbre vit sur disque et doit être mis à jour à chaque insert, update ou delete d'une colonne indexée.

Quoi indexer

Indexez les colonnes sur lesquelles la base doit chercher ou trier : celles de vos clauses WHERE, de vos conditions de JOIN, et de votre ORDER BY. Une clé étrangère sur laquelle vous joignez a presque toujours besoin d'un index (certaines bases ne le créent pas automatiquement). Une colonne que vous sélectionnez seulement, jamais filtrez ni triez, non.

Index composites : l'ordre des colonnes compte

Un index sur (status, created_at) est trié par status d'abord, puis par created_at à l'intérieur de chaque status. Il sert une requête qui filtre sur status, ou sur status et created_at, ou qui filtre sur status et trie sur created_at. Il n'aide pas une requête qui filtre seulement sur created_at, parce que les valeurs ne sont pas dans l'ordre des dates globalement. C'est la règle du préfixe le plus à gauche : un index sur (a, b, c) aide les requêtes sur a, sur a et b, ou sur a, b et c, mais pas sur b seul.

sql
-- Sert : WHERE status = ? ORDER BY created_at
-- Sert : WHERE status = ? AND created_at > ?
-- Ne sert PAS : WHERE created_at > ?  (status non contraint)
CREATE INDEX orders_status_created ON orders (status, created_at);

Covering index

Si un index contient chaque colonne dont une requête a besoin, la base répond depuis l'index seul et ne touche jamais les lignes de la table. Un index sur (customer_id, total) couvre SELECT total FROM orders WHERE customer_id = ?. Ajouter une colonne à un index pour le rendre couvrant est un gain courant et peu cher pour une requête chaude.

Sélectivité : toutes les colonnes ne valent pas un index

Un index aide en proportion du nombre de lignes qu'il élimine. Une colonne à deux valeurs (un booléen, un statut avec 90 % dans un état) n'élimine presque rien, donc la base ignore souvent l'index et scanne quand même. Les colonnes à forte cardinalité (un email, un id utilisateur, un timestamp) sont là où les index paient.

Quand un index nuit

Chaque index est une copie de certaines colonnes qui doit être gardée synchronisée. Chaque insert met à jour chaque index de la table ; chaque update d'une colonne indexée déplace une entrée dans l'arbre. Une table avec une douzaine d'index a des écritures lentes. Indexez pour les requêtes que vous lancez vraiment, supprimez les index que rien n'utilise, et n'en ajoutez pas de façon spéculative.

Lire le plan de requête

Lancez EXPLAIN sur une requête lente. Vous cherchez un index scan ou un index seek plutôt qu'un scan séquentiel ou complet de la table, et une estimation de lignes petite. Si le plan montre un scan complet sur une grosse table où vous attendiez un index, l'index est manquant, inutilisable pour cette requête (mauvais ordre de colonnes), ou la colonne n'est pas assez sélective.

Types spéciaux

  • Index unique : impose l'unicité et sert les recherches ; une clé primaire en est un.
  • Index partiel (PostgreSQL) : indexe seulement les lignes qui matchent une condition, par exemple WHERE deleted_at IS NULL, en le gardant petit.
  • Index fonctionnel / d'expression : indexe le résultat d'une expression, par exemple LOWER(email), pour qu'une recherche insensible à la casse l'utilise.
  • GIN / GiST (PostgreSQL) : pour le containment JSONB, la recherche full-text et la correspondance trigramme, pas l'égalité simple.

Pattern de requête, index à ajouter

Pattern de requêteIndex
WHERE email = ?Index sur (email), unique si ça doit l'être
WHERE user_id = ? ORDER BY created_at DESCComposite (user_id, created_at)
JOIN orders ON orders.customer_id = customers.idIndex sur orders.customer_id
SELECT total WHERE customer_id = ?Covering (customer_id, total)
WHERE LOWER(email) = ?Index fonctionnel sur LOWER(email)
WHERE deleted_at IS NULL AND status = ?Index partiel sur (status) WHERE deleted_at IS NULL

FAQ

Comment fonctionne un index de base de données ?
C'est une structure triée séparée, généralement un B-tree équilibré, contenant les valeurs de la colonne indexée dans l'ordre avec des pointeurs vers les lignes. Parce que les valeurs sont triées, la base trouve une correspondance en quelques étapes au lieu de scanner chaque ligne. Le coût est l'espace disque et des écritures plus lentes, puisque l'arbre se met à jour dès qu'une colonne indexée change.
Quelles colonnes faut-il indexer ?
Les colonnes sur lesquelles la base cherche ou trie : celles des clauses WHERE, des conditions de JOIN et des ORDER BY. Les clés étrangères sur lesquelles vous joignez ont presque toujours besoin d'un index. Les colonnes que vous sélectionnez seulement et ne filtrez ni ne triez jamais, non.
Dans quel ordre mettre les colonnes d'un index composite ?
La plus sélective et la plus fréquemment contrainte en premier, en suivant la façon dont vos requêtes filtrent. Un index sur (a, b) sert les requêtes sur a, ou sur a et b, mais pas sur b seul. Mettez la colonne sur laquelle vous filtrez toujours en premier et celle sur laquelle vous faites un range-scan ou un tri en second.
Trop d'index, est-ce un problème ?
Oui. Chaque index doit être mis à jour à chaque écriture sur la table, donc une table avec beaucoup d'index a des inserts et updates lents, et ils consomment disque et mémoire. Gardez les index que vos requêtes utilisent, supprimez ceux que rien ne touche, et n'en ajoutez pas de façon spéculative.
Comment savoir si une requête utilise un index ?
Lancez EXPLAIN dessus. Le plan devrait montrer un index scan ou seek plutôt qu'un scan séquentiel ou complet de la table, avec une estimation de lignes petite. Un scan complet où vous attendiez un index signifie que l'index est manquant, a le mauvais ordre de colonnes pour cette requête, ou que la colonne n'est pas assez sélective pour valoir la peine d'être utilisée.

Un index est un raccourci trié : il rend les lectures rapides et les écritures un peu plus lentes, et seulement pour les requêtes dont il matche les colonnes. Indexez les colonnes que vous filtrez, joignez et triez, mettez le bon ordre pour les composites, vérifiez le plan avec EXPLAIN, et supprimez les index que rien n'utilise.

Besoin d'aide sur ce sujet ? Audit Technique

Découvrir ce service