Un index de base de données est une structure de recherche triée qui permet au moteur de trouver les lignes directement au lieu de parcourir toute la table. L’analogie du livre est exacte : personne ne trouve « idempotence » dans un ouvrage de 900 pages en le lisant — on consulte l’index et on saute à la page. Les bases de données font le même choix à chaque requête, et le fait qu’elles puissent sauter ou non est la différence la plus courante entre une requête de 5 millisecondes et une requête de 5 secondes.
Points clés
| Question | Réponse |
|---|---|
| Ce que c’est | Une structure annexe triée : valeurs + pointeurs, comme l’index d’un livre |
| Le gain | Une recherche logarithmique au lieu d’un parcours linéaire — décisif à l’échelle |
| L’impôt | Chaque écriture met à jour chaque index ; le stockage grossit |
| Quoi indexer | Les colonnes de filtre, de jointure/pointeur et de tri — si elles sont sélectives |
| Le diagnostic | EXPLAIN : un parcours complet sur une grande table est le signal |
L’index, créé et ressenti
-- Le cheval de trait : un index composite calqué sur la requête chaude
CREATE INDEX orders_pending ON orders (status, created_at);
-- Des spécialisations de la même idée :
CREATE UNIQUE INDEX users_email ON users (email); -- contrainte + vitesse
CREATE INDEX unshipped ON orders (created_at)
WHERE shipped = false; -- partiel : le sous-ensemble chaud seulement
-- L'avant/après, via le plan :
EXPLAIN SELECT * FROM orders WHERE status = 'pending' AND created_at > now() - interval '1 day';
-- avant : Seq Scan on orders (lignes examinées : 4 812 309)
-- après : Index Scan using orders_pending (lignes examinées : 1 214)
Le code applicatif rencontre la même physique à travers la forme de la requête — cette requête est la spécification de l’index (status, createdAt), quelle que soit la surface qui l’écrit :
// JavaScript / Node.js — Back4app JS SDK
// The query shape tells you the index: (status, createdAt)
const query = new Parse.Query('Order');
query.equalTo('status', 'pending'); // equality first…
query.greaterThan('createdAt', since); // …then the range
query.descending('createdAt'); // …sorted by the same column
const backlog = await query.find();
// Indexed: milliseconds at any size. Unindexed: a full collection scan. // Flutter / Dart — Back4app Flutter SDK
// The query shape tells you the index: (status, createdAt)
final query = QueryBuilder<ParseObject>(ParseObject('Order'))
..whereEqualTo('status', 'pending') // equality first…
..whereGreaterThan('createdAt', since) // …then the range
..orderByDescending('createdAt'); // …sorted by the same column
final response = await query.query();
// Indexed: milliseconds at any size. Unindexed: a full collection scan. // iOS / Swift — Back4app Swift SDK
// The query shape tells you the index: (status, createdAt)
let query = Order.query("status" == "pending", "createdAt" > since)
.order([.descending("createdAt")])
query.find { result in
if case .success(let backlog) = result { render(backlog) }
}
// Indexed: milliseconds at any size. Unindexed: a full collection scan. // Android / Kotlin — Back4app Android SDK
// The query shape tells you the index: (status, createdAt)
val query = ParseQuery.getQuery<ParseObject>("Order")
query.whereEqualTo("status", "pending") // equality first…
query.whereGreaterThan("createdAt", since) // …then the range
query.orderByDescending("createdAt") // …sorted by the same column
query.findInBackground { backlog, e -> if (e == null) render(backlog) }
// Indexed: milliseconds at any size. Unindexed: a full collection scan. Comment fonctionne le saut
La structure par défaut partout est le B-tree : peu profond, trié, auto-équilibré — trois ou quatre niveaux couvrent des centaines de millions d’entrées, et ses feuilles sont chaînées, ce qui explique qu’une seule structure serve aussi bien l’égalité, les plages et ORDER BY. Cette polyvalence est la raison pour laquelle « dans le doute, B-tree » est le conseil permanent, les alternatives jouant les spécialistes.
B-tree vs. hash vs. les spécialistes
Quel type d’index devriez-vous utiliser ?
| Type | Sert | Choisissez-le quand |
|---|---|---|
| B-tree (défaut) | =, <, >, plages, tris, préfixes | Presque toujours — le généraliste |
| Hash | Égalité seulement, O(1) | Recherches par correspondance exacte, rien d’autre |
| Composite | Multi-colonnes, préfixe gauche | La requête chaude filtre sur plusieurs colonnes |
| Unique | B-tree + pas de doublons | Contrainte et index en un |
| Partiel | Une tranche filtrée de lignes | Sous-ensembles chauds : non expédié, non lu, actif |
| Couvrant | Requête servie depuis le seul index | Requêtes en lecture intensive avec une liste de colonnes stable |
| Full-text (inversé) | Mots → documents | Champs de recherche |
| Géospatial | Points, régions, distance | Requêtes « près de moi » |
Deux règles portent la ligne du composite : la règle du préfixe gauche — un index sur (a, b, c) sert a, (a, b), (a, b, c), jamais b seul — et l’égalité d’abord, la plage/le tri en dernier dans l’ordre des colonnes (le mnémonique ESR de l’indexation des bases documentaires, où les mêmes B-trees font le même travail).
Quand indexer — et quand s’abstenir
Indexez : les colonnes des filtres WHERE fréquents ; les clés de jointure et les champs pointeur (certains moteurs n’indexent jamais les clés étrangères automatiquement — un classique du parcours silencieux) ; les colonnes ORDER BY sur les chemins chauds ; et toujours avec la sélectivité en tête — une colonne email (des millions de valeurs distinctes) mérite sa place, un drapeau de statut (trois valeurs) généralement pas à lui seul, même s’il brille en tête d’un composite avec une plage derrière. N’indexez pas : les petites tables que le moteur parcourt plus vite qu’il ne les cherche ; les tables chaudes en écriture au-delà des quelques index essentiels ; les colonnes déjà servies par le préfixe gauche d’un index existant ; et tout ce pour quoi vous ne pouvez pas nommer une requête — un index sans requête est un pur impôt d’écriture. La boucle d’audit : passez EXPLAIN sur les requêtes lentes pour repérer les parcours, vérifiez les statistiques d’utilisation pour les index morts, ajoutez et supprimez en conséquence.
Cas d’usage courants
- La requête de liste chaude. Filtres statut + date derrière chaque dashboard et chaque feed — le terrain de jeu de l’index composite.
- Champs de login et de recherche. Email, nom d’utilisateur, identifiants externes — des index uniques qui font contrainte et vitesse à la fois.
- Chemins de jointure et de pointeur. Chaque clé étrangère et chaque pointeur que vos requêtes traversent ; le complice silencieux du problème N+1 est l’un d’eux resté sans index.
- Filtres multi-tenant. Des composites menés par le tenant — la règle de l’architecture multi-tenant selon laquelle chaque index commence par
tenant_id. - Recherche et géo. Des index inversés et spatiaux qui propulsent les requêtes que les B-trees ne savent pas servir.
Devriez-vous ajouter cet index ? Matrice de décision
| Ajoutez-le quand… | Passez votre chemin quand… |
|---|---|
| Une requête fréquente filtre ou trie sur la colonne | Aucune requête de production ne l’utilise |
| EXPLAIN montre des parcours sur une table qui grossit | La table est petite et le reste |
| La colonne est sélective (beaucoup de valeurs distinctes) | Le préfixe d’un index existant la couvre déjà |
| C’est une clé de jointure ou un champ pointeur | La table est dominée par les écritures et la lecture est rare |
| Une règle d’unicité doit être imposée de toute façon | Vous devinez — mesurez d’abord |
La discipline en une phrase : les index se créent en réponse à des requêtes, se passent en revue à l’aune de l’usage, et se suppriment sans sentimentalisme.
Limites et trade-offs
- Les écritures paient pour les lectures. Chaque index est une structure de plus que chaque écriture doit maintenir — les chargements en masse tournent notoirement plus vite avec les index supprimés puis reconstruits.
- Le stockage est bien réel. Les index rivalisent couramment avec la taille de la table elle-même ; les index couvrants, surtout, échangent du disque contre de la vitesse.
- C’est l’optimiseur qui décide, pas vous. Un index peu sélectif peut être ignoré à juste titre ; des statistiques périmées peuvent en ignorer un bon à tort — le plan est la vérité.
- Les erreurs d’ordre neutralisent les composites.
(created_at, status)et(status, created_at)sont des outils différents ; la règle du préfixe gauche ne pardonne rien. - Un index ne répare pas la requête. Les jokers en tête, les fonctions appliquées aux colonnes et les boucles N+1 court-circuitent l’indexation par le haut — la forme de la requête passe d’abord.
Les index sur Back4app
Back4app est une plateforme open-source de Backend as a Service (BaaS) qui combine une base de données gérée, des API REST et GraphQL générées automatiquement, l’authentification, le stockage de fichiers et des fonctions serverless avec Cloud Code. La gestion des index vit avec le schéma dans le dashboard : créez, passez en revue et supprimez les index par classe — mono-champ ou composés — sur le stockage propulsé par MongoDB que vos requêtes SDK interrogent réellement, en suivant la même logique B-tree et ESR décrite ci-dessus. La requête des onglets de code et l’index qui la sert se conçoivent au même endroit, précisément là où la discipline « indexez ce que vous interrogez » veut vivre.
Questions fréquentes
Qu'est-ce qu'un index de base de données, en termes simples ?
Une structure séparée et triée — généralement un B-tree — qui contient des valeurs de colonne plus des pointeurs vers leurs lignes, exactement comme l'index d'un livre contient des termes plus des numéros de page. Le moteur cherche la valeur dans la petite structure triée et saute directement aux lignes correspondantes, au lieu de lire la table entière en espérant les trouver.
Une requête indexée est-elle beaucoup plus rapide ?
C'est la différence entre un travail logarithmique et un travail linéaire : une recherche dans un B-tree touche une poignée de pages, que la table contienne dix mille lignes ou cent millions, alors qu'un parcours complet lit tout. À petite échelle, les deux paraissent instantanés — c'est pourquoi les index manquants se cachent en développement et explosent en production, où la même requête examine soudain des millions de lignes.
Les index ralentissent-ils les écritures ?
Oui — c'est l'impôt. Chaque insertion, mise à jour ou suppression sur une colonne indexée doit aussi mettre à jour chaque index qui la référence : six index sur une table, c'est jusqu'à six mises à jour de structure supplémentaires par écriture, plus le stockage qu'ils occupent. Les index sont un achat de vitesse de lecture payé en vitesse d'écriture et en disque — les index délibérés le méritent, les index oubliés se contentent de vous facturer.
Quelles colonnes faut-il indexer ?
Les colonnes que vos requêtes utilisent réellement : les filtres (WHERE), les clés de jointure — y compris les clés étrangères et les champs pointeur, que certains moteurs n'indexent pas automatiquement — et les colonnes de tri (ORDER BY). Ajoutez le test de sélectivité : une colonne email qui distingue des millions de lignes mérite son index ; un drapeau booléen qui coupe la table en deux, généralement pas.
Dans quel ordre placer les colonnes d'un index composite ?
L'égalité d'abord, la plage et le tri ensuite — et souvenez-vous de la règle du préfixe gauche : un index sur (a, b, c) sert les requêtes qui filtrent sur a, sur a et b, ou sur les trois, mais pas sur b seul. La formulation de la même règle côté bases documentaires est ESR : Equality, Sort, Range. L'ordre des colonnes fait la différence entre un index composite qui fonctionne et un index qui se contente d'exister.
Comment trouver les index manquants ou inutilisés ?
Pour les manquants : lancez le plan de requête — EXPLAIN — et cherchez les parcours complets sur les grandes tables ; la colonne de filtre d'une requête lente et fréquente est la candidate. Pour les inutilisés : chaque moteur tient des statistiques d'utilisation des index, et un index qu'aucune requête n'a touché depuis des mois est un pur impôt d'écriture — supprimez-le. Le plan et les statistiques, ensemble, constituent toute la méthodologie.
Que sont les index uniques, partiels et couvrants ?
Des spécialisations de la même structure : un index unique impose l'absence de doublons comme contrainte tout en accélérant les recherches ; un index partiel ne couvre que les lignes qui satisfont une condition — petit et rapide pour les sous-ensembles chauds comme les commandes non expédiées ; un index couvrant contient toutes les colonnes dont une requête a besoin, ce qui permet au moteur de répondre depuis le seul index sans visiter la table.
L'indexation fonctionne-t-elle de la même façon dans les bases documentaires ?
Conceptuellement identique — des B-trees sur des valeurs de champs — avec les mêmes trade-offs et la même logique de préfixe gauche sous la règle empirique ESR. Les bases documentaires ajoutent des index multikey sur les champs tableau et des variantes géospatiales, et la règle opérationnelle survit à la traduction : indexez ce que vous interrogez, surtout les champs pointeur que les jointures et les includes traversent.