CHAPITRE 8 : B+ ARBRES, BASES DE DONNÉES ET SYSTÈMES DE FICHIERS

Presque aucune base de données n'utilise le B-arbre tel qu'on vient de l'étudier. Toutes emploient une variante : le B+ arbre : qui corrige deux défauts précis. Les comprendre, c'est comprendre pourquoi un CREATE INDEX accélère certaines requêtes et pas d'autres.

Les deux défauts du B-arbre classique

Premier défaut : les parcours d'intervalle sont pénibles. Pour lister toutes les clés entre deux bornes, il faut remonter chez le père puis redescendre à chaque changement de feuille. Sur une requête du type WHERE date BETWEEN … AND …, c'est désastreux.

Second défaut : les nœuds internes portent des données. Chaque clé d'un nœud interne traîne son enregistrement, ce qui réduit le nombre de clés par page, ce qui augmente la hauteur.

Les deux idées du B+ arbre

1. Toutes les données descendent dans les feuilles. Les nœuds internes ne contiennent que des clés servant d'aiguillage, sans aucune donnée associée. Une clé peut d'ailleurs apparaître à la fois comme séparateur en haut et comme donnée en bas.

2. Les feuilles sont chaînées entre elles. Chaque feuille pointe vers la suivante, formant une liste triée qu'on parcourt sans jamais remonter.

Ce que cela change

B-arbreB+ arbre
Donnéesdans tous les nœudsdans les feuilles seulement
Recherche d'une clépeut s'arrêter en chemin descend toujours jusqu'à une feuille
Parcours d'intervalleremontées et redescentes un simple parcours de liste
Clés par nœud internemoinsbeaucoup plus
Hauteurplus grandeplus petite

La recherche d'une clé isolée est légèrement moins bonne, puisqu'on ne peut plus s'arrêter dans un nœud interne. En échange, l'arbre est plus plat et les parcours deviennent triviaux. Pour une base de données, le marché est excellent.

Un B+ arbre, c'est un index au-dessus d'une liste triée.

Lire un plan de requête

Cette structure explique directement le comportement des bases de données.

-- rapide : descente dans l'index, 3 a 4 acces disque
SELECT * FROM clients WHERE id = 4218;

-- rapide : une descente, puis un parcours de la liste chainee
SELECT * FROM commandes WHERE date BETWEEN '2026-01-01' AND '2026-03-31';

-- rapide : l'index est deja trie, aucun tri a faire
SELECT * FROM clients ORDER BY nom LIMIT 20;

-- LENT : l'index est trie sur nom, pas sur ce qui suit le %
SELECT * FROM clients WHERE nom LIKE '%dupont';

La dernière requête est le cas d'école. Un index trié sur nom permet de trouver tout ce qui commence par une chaîne, puisque c'est un intervalle. Mais tout ce qui finit par une chaîne ne forme aucun intervalle : l'index ne sert à rien, et la base parcourt toute la table.

La règle du préfixe. Un index B+ accélère les conditions qui se traduisent par un intervalle sur l'ordre des clés : égalité, comparaison, BETWEEN, LIKE 'debut%', et ORDER BY sur la colonne indexée. Il ne sert à rien dès qu'on applique une fonction à la colonne (WHERE UPPER(nom) = …) ou qu'on cherche un suffixe.

Index composés

Un index peut porter sur plusieurs colonnes à la fois : les clés sont alors comparées dans l'ordre lexicographique.

CREATE INDEX idx ON commandes (client_id, date);

-- utilise l'index pleinement
WHERE client_id = 42 AND date > '2026-01-01'

-- utilise l'index partiellement (seulement client_id)
WHERE client_id = 42

-- n'utilise PAS l'index : la premiere colonne manque
WHERE date > '2026-01-01'

La raison est géométrique : les clés sont triées d'abord par client_id, puis par date. Sans contrainte sur la première colonne, les lignes cherchées sont éparpillées dans tout l'index. C'est exactement comme chercher tous les mots dont la deuxième lettre est un K dans un dictionnaire.

Où l'on en trouve

DomaineExemples
Bases relationnellesPostgreSQL, MySQL (InnoDB), Oracle, SQL Server, SQLite
Systèmes de fichiersNTFS (1993), ext4 (2006), APFS (2017), Btrfs, XFS, HFS+
Bases clé-valeurBerkeley DB, LMDB

Un système de fichiers fait le même travail qu'un index : retrouver un fichier par son nom dans un répertoire qui en contient des centaines de milliers, avec le moins d'accès disque possible. Le problème est identique, la solution aussi.

Et les autres structures ?

StructureRechercheIntervalleBon pour
Table de hachageO(1)impossible égalité seule, en mémoire
ABR équilibré (AVL, rouge-noir)O(log₂ n)correct données en mémoire
B+ arbreO(logt n) excellentdonnées sur disque
LSM-treeO(log n) ×correct écritures massives

La table de hachage bat le B+ arbre sur la recherche d'égalité pure, c'est du O(1). Mais elle ne sait faire aucun parcours trié, aucun intervalle, aucun ORDER BY. Pour un index de base de données, cela la disqualifie dans la plupart des usages.

Les LSM-trees, eux, sont le concurrent sérieux du moment (LevelDB, RocksDB, Cassandra) : ils écrivent séquentiellement plutôt que de modifier des pages en place, ce qui convient mieux aux SSD et aux charges d'écriture intensives. Ils lisent en revanche moins bien.

À retenir

  • Un B+ arbre ne garde les données que dans les feuilles, et les chaîne entre elles.
  • Nœuds internes plus légers donc arbre plus plat ; parcours d'intervalle immédiat.
  • Un index accélère ce qui se traduit par un intervalle : égalité, comparaison, préfixe, tri.
  • Index composé : la première colonne commande, sans elle l'index est inutilisable.
  • Tous les systèmes de fichiers et toutes les bases relationnelles sérieuses en utilisent.

Exercices d'application

Exercice 1
Une table de 20 millions de lignes possède un index B+ sur (pays, ville). Lesquelles de ces requêtes utilisent l'index, et pourquoi ?
(a) WHERE pays = 'Cameroun'
(b) WHERE pays = 'Cameroun' AND ville = 'Douala'
(c) WHERE ville = 'Douala'
(d) WHERE pays LIKE 'Cam%'
(e) ORDER BY pays, ville
Voir le corrigeMasquer le corrige

(a) Oui, partiellement. Les clés sont triées d'abord par pays : toutes les lignes du Cameroun forment un intervalle contigu. L'index les trouve directement.

(b) Oui, pleinement. Les deux colonnes sont contraintes dans l'ordre de l'index : la descente mène exactement aux lignes voulues.

(c) Non. Sans contrainte sur pays, les lignes de Douala sont dispersées dans tout l'index : une fois par pays ayant une ville de ce nom. Elles ne forment aucun intervalle. La base fera un parcours complet.

(d) Oui. LIKE 'Cam%' est une recherche par préfixe, donc un intervalle : tout ce qui est compris entre « Cam » et « Can ».

(e) Oui. L'index est déjà stocké dans cet ordre exact : la base le parcourt séquentiellement et n'a aucun tri à effectuer. C'est souvent le gain le plus spectaculaire d'un index.

Exercice 2
Pourquoi une table de hachage, pourtant en O(1), est-elle un mauvais choix pour l'index principal d'une base de données ?
Voir le corrigeMasquer le corrige

Parce que le O(1) ne porte que sur l'égalité, et qu'une base de données fait bien d'autres choses.

Une table de hachage disperse volontairement les clés : deux clés voisines atterrissent à des endroits sans rapport. Elle perd donc tout l'ordre, et avec lui :

  • les intervalles (BETWEEN, >, <) ;
  • le tri (ORDER BY sans tri supplémentaire) ;
  • les recherches par préfixe ;
  • le minimum et le maximum.

S'y ajoute un défaut propre au disque : le redimensionnement. Quand la table se remplit, il faut la reconstruire entièrement et réécrire tout l'index : inenvisageable sur plusieurs gigaoctets en production.

C'est pourquoi PostgreSQL propose bien des index HASH, mais que le type par défaut est BTREE. On réserve le hachage aux colonnes qu'on n'interroge qu'en égalité stricte.

Exercice 3
Un B+ arbre de degré t = 200 indexe 100 millions de lignes. Les feuilles contiennent les clés et un pointeur vers la ligne. Estimez le nombre d'accès disque pour lire 1 000 lignes consécutives.
Voir le corrigeMasquer le corrige

Descente. La hauteur vaut au plus :

log_200(10^8 / 2) = log(5x10^7) / log(200)
                   = 7,70 / 2,30
                   = 3,35  ->  4 niveaux

La racine étant en mémoire, cela fait 3 accès.

Parcours. Une feuille contient entre t − 1 = 199 et 2t − 1 = 399 clés ; comptons un remplissage réaliste d'environ 270. Pour 1 000 lignes :

1000 / 270 = 3,7  ->  4 feuilles

Les feuilles étant chaînées, chacune coûte un seul accès : 4 accès.

3 + 4 = 7 accès disque pour 1 000 lignes

Soit 0,007 accès par ligne. À titre de comparaison, un parcours complet de la table lirait des centaines de milliers de pages. Et si l'index contenait aussi les colonnes demandées : un index couvrant : on éviterait même les 1 000 accès aux lignes elles-mêmes.

Cherchez avant d'ouvrir le corrige : c'est en butant sur les difficultes que l'on progresse.

Questions / Réponses

Aucune question. Soyez le premier à poser une question.
★ ★ ★ ★ ★
2 votes. Moyenne 5 sur 5.

Ajouter un commentaire

Anti-spam