Read in English · Leer en español · Auf Deutsch lesen
Section 0.5 sur 0.8 · Débutant · 18 min de lecture
Fichiers et bases de données : stockage relationnel et non relationnel, requêtes et transactions
Leçon vidéo
5 min · narré en anglais, avec sous-titresÉcouter seulement (audio)
Lire la transcription
Un programme en cours d’exécution garde ses données de travail en mémoire, et celle-ci oublie tout quand le programme s’arrête. Tout ce qui doit survivre, comme le compte d’un client ou une commande, doit être écrit sur un support de stockage. Imaginez une base de données comme une bibliothèque tenue par un bibliothécaire soigneux. Elle tient un catalogue, vérifie que chaque enregistrement respecte les règles et ne perd jamais une modification qu’elle a promis de conserver.
La façon la plus simple de garder des données est un fichier. Les fichiers sont excellents pour ce qui forme naturellement un document entier, comme les images, les fichiers PDF, les vidéos et les journaux. Mais imaginez une boutique qui garde toutes ses commandes dans un seul fichier de tableur. Deux clients passent au paiement au même moment. Quelle modification l’emporte ? Le serveur tombe en panne au milieu d’un enregistrement. Le fichier est-il maintenant à moitié ancien et à moitié nouveau ? Et quelqu’un saisit un numéro de client qui n’existe pas. Qui l’en empêche ? Chaque programme utilisant le fichier devrait résoudre lui-même chacun de ces problèmes.
Un système de gestion de base de données résout ces problèmes une fois pour toutes, pour tout le monde. Les applications, comme l’application web d’une boutique et un outil de reporting, ne touchent jamais directement aux fichiers de données. Elles envoient des requêtes, écrites dans un langage appelé SQL. Le système de gestion de base de données vérifie les règles, planifie la façon de répondre à chaque question et gère de nombreux utilisateurs à la fois. Il écrit les données dans des fichiers sur disque et enregistre chaque modification, dans l’ordre, dans un journal. Et des copies des deux sont envoyées régulièrement vers un stockage de sauvegarde situé dans un lieu séparé.
Le type le plus utilisé est la base de données relationnelle. Les données sont rangées dans des tables. Chaque table décrit un type de chose. Chaque ligne est un exemplaire, par exemple un client, et chaque colonne est un fait à son sujet, par exemple son adresse e-mail. Chaque table a une clé primaire, une valeur unique pour chaque ligne, comme l’identifiant du client. Une commande contient elle aussi un identifiant de client. C’est une clé étrangère : elle pointe vers une ligne de la table des clients. La base de données refusera une commande pour un client qui n’existe pas. Chaque fait est stocké une seule fois. Une commande peut contenir de nombreux produits ; une table de liaison, les lignes de commande, enregistre donc une ligne par produit et par commande. Répondre à une question oblige alors à combiner des tables, ce que SQL fait avec une jointure.
Comment la base de données trouve-t-elle rapidement les données ? Supposons qu’une table des commandes compte dix millions de lignes, et qu’un écran du service client ait besoin des vingt commandes d’un client. Sans aide, la base de données doit lire chaque ligne, une par une, et la vérifier. C’est ce qu’on appelle un parcours complet de la table. Un index fonctionne comme l’index à la fin d’un livre. C’est un arbre trié, de trois ou quatre niveaux seulement, si bien que la base de données lit une poignée de pages et va droit aux vingt lignes. Si la table devient dix fois plus grande, le parcours fait dix fois plus de travail. L’index, lui, ajoute au plus un niveau. Mais les index ne sont pas gratuits. Chaque nouvelle commande doit aussi mettre à jour l’index, et celui-ci prend de la place. C’est encore cet échange d’espace contre de la vitesse.
Virer cent livres d’Alice à Bob demande deux modifications. Si le serveur tombe en panne entre les deux, l’argent disparaît. Une transaction fait réussir les modifications ensemble, ou pas du tout. Ses garanties s’appellent ACID. L’atomicité, c’est tout ou rien. La cohérence, c’est que les règles ne restent jamais enfreintes. Une règle interdisant aux soldes de passer sous zéro rejette un virement qu’Alice ne peut pas se permettre. L’isolation, c’est que deux virements exécutés en même temps ne peuvent pas s’appuyer tous les deux sur le même ancien solde. Et la durabilité, c’est que lorsque l’application affiche « virement effectué », la modification a vraiment été enregistrée et survit à une panne ou à une coupure de courant.
Les bases de données relationnelles ne sont pas la seule option. NoSQL est un nom générique pour les bases de données qui n’utilisent pas les tables de la même façon. Les bases orientées documents stockent des enregistrements autonomes, ce qui convient par exemple à un catalogue de produits. Les bases clé-valeur sont des dictionnaires géants, pour des recherches très rapides, comme les paniers d’achat. Les bases orientées colonnes larges traitent d’énormes volumes d’écritures, comme des relevés de capteurs, répartis sur de nombreuses machines. Et les bases orientées graphe répondent aux questions sur les liens. Beaucoup de systèmes réels utilisent plus d’un type de base, chacun là où il convient.
Les disques tombent en panne et les gens font des erreurs ; les données ont donc besoin de sauvegardes conservées dans un endroit séparé. Et une sauvegarde qui n’a jamais été restaurée est un espoir, pas une sauvegarde. Une réplique, une seconde base de données qui copie la première, protège contre la panne d’une machine. Mais une suppression malencontreuse atteint la réplique en quelques millisecondes. Enfin, les données survivent en général au code qui les entoure. Les applications sont réécrites tous les quelques ans ; les dossiers clients sont conservés pendant des décennies. C’est pourquoi les décisions sur l’endroit où se trouvent les données comptent parmi les plus coûteuses à remettre en cause.
La section complète contient de vraies requêtes SQL, un virement bancaire qui fonctionne et plus de détails sur le schéma de la boutique. Ensuite, dans la section zéro point six, nous verrons les API et la façon dont des systèmes séparés travaillent ensemble.
Ce qu’il faut savoir
- Les fichiers conviennent aux documents entiers, comme les images et les journaux ; les bases de données conviennent aux données structurées que de nombreux utilisateurs lisent et modifient en même temps, car le SGBD gère la concurrence, les règles, la recherche rapide et la reprise après une panne.
- Les bases de données relationnelles stockent les données dans des tables faites de lignes et de colonnes ; une clé primaire identifie chaque ligne et une clé étrangère relie une ligne à une ligne d’une autre table.
- SQL est déclaratif : vous indiquez le résultat voulu, par exemple un SELECT filtré ou un JOIN entre plusieurs tables, et le planificateur de requêtes de la base de données décide comment l’obtenir.
- Un index permet à la base de données de trouver des lignes en O(log n) étapes au lieu de parcourir toute la table, au prix d’un stockage supplémentaire et d’écritures plus lentes.
- Une transaction fait réussir ou échouer ensemble plusieurs modifications ; ACID (atomicité, cohérence, isolation, durabilité) décrit ses garanties, comme dans un virement bancaire qui ne peut jamais se faire à moitié.
- Les familles NoSQL (documents, clé-valeur, colonnes larges et graphes) conviennent à des formes de données et à des modes d’accès particuliers ; de nombreux systèmes utilisent une base relationnelle plus une ou plusieurs de ces bases.
- Une sauvegarde n’est prouvée que lorsqu’on la restaure, les répliques ne protègent pas contre les erreurs, et les bases de données managées dans le cloud prennent en charge l’exploitation, mais pas la conception du schéma et des requêtes.
Pourquoi les programmes ont besoin d’un endroit où garder les données
Un programme en cours d’exécution garde ses données de travail en mémoire (RAM), qui est rapide mais oublie tout quand le programme s’arrête ou que la machine redémarre (voir ce qu’est un logiciel). Tout ce qui doit survivre à un redémarrage, comme le compte d’un client, une commande ou un rendez-vous à l’hôpital, doit être écrit sur un support qui conserve son contenu sans alimentation : un disque dur ou un SSD. Les données qui survivent ainsi sont dites persistantes, et décider comment les rendre persistantes est l’une des premières décisions de tout système, et l’une de celles qui durent le plus longtemps.
La façon la plus simple de rendre des données persistantes est un fichier : une suite d’octets portant un nom, que le système d’exploitation stocke sur disque. Les fichiers sont excellents pour ce qui forme naturellement un document entier : images, PDF, vidéos, configuration, journaux, rapports exportés. Les systèmes les conservent souvent dans un stockage objet dans le cloud (un service qui stocke des fichiers par leur nom) et n’enregistrent ailleurs qu’une référence.
Les fichiers deviennent pénibles quand de nombreux utilisateurs lisent et modifient en même temps de petits morceaux de données structurées. Imaginez une boutique qui garde toutes ses commandes dans un seul fichier de tableur. Deux clients passent au paiement au même moment : quelle modification l’emporte ? Le serveur tombe en panne au milieu d’un enregistrement : le fichier est-il à moitié ancien, à moitié nouveau ? Une personne du service client veut les commandes d’un seul client : le programme doit-il lire tout le fichier ? Quelqu’un saisit un numéro de client qui n’existe pas : qui l’en empêche ? Chaque programme utilisant le fichier devrait résoudre chacun de ces problèmes, et de la même façon.
Ce qu’est une base de données
Une base de données est un ensemble organisé de données, et le programme qui s’en occupe est un système de gestion de base de données (SGBD), comme PostgreSQL, MySQL, Microsoft SQL Server, Oracle Database ou SQLite. On dit souvent « la base de données » pour les deux. Le SGBD résout les problèmes des fichiers une fois pour toutes, pour tout le monde : il permet à de nombreux utilisateurs de travailler en même temps sans se marcher sur les pieds, trouve les données rapidement, refuse les données qui enfreignent les règles et revient à un état correct après une panne.
L’analogie est celle d’une bibliothèque. Une pile de papiers sur un bureau suffit si vous êtes seul à l’utiliser. Une bibliothèque a un catalogue, des règles sur l’emplacement de chaque chose, un registre de qui a emprunté quoi, et un bibliothécaire qui ne laissera pas deux personnes prendre le même exemplaire. Les applications ne touchent pas aux rayonnages : elles s’adressent au bibliothécaire.
Dans la plupart des systèmes d’entreprise, le SGBD tourne comme un processus serveur à part, souvent sur sa propre machine, et les applications lui parlent par le réseau (voir comment les ordinateurs communiquent). SQLite est une exception bien connue : c’est une bibliothèque qui s’exécute à l’intérieur de l’application et stocke toute la base de données dans un seul fichier, ce qui explique qu’on la trouve dans les téléphones, les navigateurs et les petits outils.
Où les données se trouvent réellement
Bases de données relationnelles : tables, clés et relations
Le type de base de données le plus utilisé est la base de données relationnelle, fondée sur un modèle publié par Edgar F. Codd, d’IBM, en 1970. Les données sont rangées dans des tables. Chaque table décrit un type de chose (clients, commandes, produits). Chaque ligne est un exemplaire de cette chose (un client), et chaque colonne est un fait à son sujet (nom, adresse e-mail, date d’inscription), avec un type déclaré comme entier, texte, date ou décimal.
Chaque table a une clé primaire : une colonne (ou une combinaison de colonnes) dont la valeur est unique pour chaque ligne et jamais vide, comme customer_id. Une clé étrangère est une colonne d’une table qui contient la clé primaire d’une ligne d’une autre table. Une commande a une colonne customer_id, qui indique quel client l’a passée. La déclarer comme clé étrangère indique au SGBD de refuser une commande pour le client 999 s’il n’existe pas de client 999 : c’est ce qu’on appelle l’intégrité référentielle.
L’ensemble des tables, colonnes, types, clés et règles forme le schéma. Le schéma est un contrat : chaque programme qui utilise la base de données peut compter sur le fait qu’une commande a toujours un client valide et un prix qui est un nombre. Modifier un schéma plus tard, quand des millions de lignes et plusieurs programmes en dépendent, est possible mais demande du soin ; c’est pourquoi l’évolution des schémas a sa propre section au chapitre 7.
Les conceptions relationnelles cherchent à stocker chaque fait une seule fois. L’adresse e-mail du client se trouve dans une ligne de la table des clients, et n’est pas recopiée dans chaque commande. C’est ce qu’on appelle la normalisation. Ainsi, une mise à jour se fait à un seul endroit et ne peut pas laisser derrière elle des copies contradictoires. Le prix de cet ordre est que répondre à une question oblige souvent à combiner des tables, et c’est à cela que servent les jointures.
Poser des questions avec SQL
SQL (Structured Query Language, prononcé « S-Q-L » ou, à l’anglaise, « sequel ») est le langage utilisé pour travailler avec les bases de données relationnelles. Il a été développé chez IBM dans les années 1970 et est devenu une norme ANSI et ISO à la fin des années 1980 ; tous les grands SGBD relationnels le parlent, chacun avec de petites différences de dialecte. SQL est déclaratif : vous décrivez le résultat voulu, pas les étapes pour le trouver. Le planificateur de requêtes du SGBD choisit les étapes, par exemple lire toute une table ou utiliser un index.
Une jointure (join) combine des lignes de deux tables ou plus en faisant correspondre des colonnes, en général une clé étrangère avec la clé primaire à laquelle elle renvoie. La seconde requête ci-dessous liste chaque ligne des commandes d’un client avec le nom du produit, en puisant dans trois tables à la fois.
-- One customer's orders since 1 October, newest first
SELECT order_id, status, placed_at
FROM orders
WHERE customer_id = 42
AND placed_at >= '2026-10-01'
ORDER BY placed_at DESC;
-- Each order line with the product's name (a join)
SELECT o.order_id, p.name, i.quantity, i.unit_price
FROM orders AS o
JOIN order_items AS i ON i.order_id = o.order_id
JOIN products AS p ON p.product_id = i.product_id
WHERE o.customer_id = 42;INSERT, UPDATE et DELETE modifient les données, et CREATE TABLE définit le schéma. Le code des applications envoie souvent le SQL par l’intermédiaire d’un outil de mapping objet-relationnel (ORM), une bibliothèque qui transforme les lignes en objets. Les ORM évitent d’écrire beaucoup de code, mais cachent le SQL réellement envoyé ; un problème de performances classique est un ORM qui envoie discrètement une requête par ligne au lieu d’une seule jointure.
Les index : l’index du livre, pas le livre entier
Sans aide, la seule façon pour la base de données de trouver toutes les commandes du client 42 est de lire chaque ligne de la table des commandes et de les vérifier une à une : un parcours complet de la table, qui est en O(n) dans la notation vue dans données et algorithmes. Un index de base de données est une structure de données supplémentaire et triée qui associe les valeurs d’une colonne aux lignes qui les contiennent, exactement comme l’index à la fin d’un livre associe des mots à des numéros de page. La plupart des bases de données relationnelles utilisent par défaut un arbre B (B-tree), un arbre large, peu profond et toujours équilibré, qui trouve une valeur en O(log n) étapes.
Les clés primaires sont indexées automatiquement. Les autres index sont ajoutés délibérément, pour les colonnes sur lesquelles les requêtes filtrent, font des jointures ou trient : CREATE INDEX idx_orders_customer ON orders (customer_id);. Les index ne sont pas gratuits. Chaque insertion, mise à jour ou suppression doit aussi mettre à jour chaque index de la table, et chaque index occupe de l’espace disque et de la mémoire. Une table avec quinze index se lit vite et s’écrit lentement. Choisir les index est un compromis guidé par les requêtes que le système exécute réellement, ce qui est un premier exemple du compromis entre espace et temps qui traverse toute l’architecture.
Exemple commenté
Exemple commenté : trouver les commandes d’un client
Une boutique a 10 millions de lignes dans sa table des commandes. L’écran du service client demande toutes les commandes d’un client, soit environ 20 lignes.
| Sans index | Parcours complet de la table : la base de données lit et vérifie les 10 000 000 lignes pour trouver les 20 qui correspondent. |
|---|---|
| Avec un index en arbre B sur customer_id | Chaque page d’index contient des centaines de clés, si bien que l’arbre n’a que trois ou quatre niveaux. La base de données lit une poignée de pages d’index, puis va chercher les 20 lignes correspondantes. |
| Croissance | Si la table passe à 100 millions de lignes, le parcours fait dix fois plus de travail ; la recherche par l’index ajoute au plus un niveau. |
| Un coût payé ailleurs | Chaque nouvelle commande écrit désormais dans la table et dans l’index. L’espace disque utilisé augmente de la taille de l’index. |
C’est O(n) contre O(log n), vu à la section 0.2 : souvent la différence entre une réponse en quelques secondes et une réponse en quelques millisecondes. Beaucoup de plaintes sur des requêtes lentes dans les jeunes systèmes se règlent avec le bon index, pas avec du matériel plus puissant.
Transactions et ACID : le virement bancaire
Virer 100 livres du compte d’Alice vers celui de Bob demande deux modifications : retirer 100 à Alice, ajouter 100 à Bob. Si le serveur tombe en panne après la première modification et avant la seconde, 100 livres ont disparu. Si deux virements depuis le compte d’Alice s’exécutent en même temps, tous deux peuvent voir un solde de 150 et réussir tous les deux, ce qui la met à découvert. Une transaction regroupe plusieurs opérations en une seule unité de travail qui soit aboutit entièrement (validation, ou commit), soit n’a aucun effet (annulation, ou rollback).
Les garanties qu’offre une transaction se résument par l’acronyme ACID, créé par Theo Härder et Andreas Reuter en 1983 à partir de travaux antérieurs de Jim Gray :
| Propriété | Ce qu’elle promet | Dans le virement bancaire |
|---|---|---|
| Atomicité | Toutes les modifications de la transaction ont lieu, ou aucune. | Alice n’est jamais débitée sans que Bob soit crédité. |
| Cohérence | Une transaction fait passer la base de données d’un état valide à un autre ; les règles comme les clés et les contrôles ne sont jamais laissées enfreintes. | Une règle interdisant aux soldes de passer sous zéro rejette un virement qu’Alice ne peut pas se permettre. |
| Isolation | Les transactions qui s’exécutent en même temps ne voient pas le travail à moitié fait des autres ; le résultat est le même que si elles s’étaient exécutées l’une après l’autre (dans une mesure fixée par le niveau d’isolation). | Deux virements simultanés depuis le compte d’Alice ne peuvent pas s’appuyer tous les deux sur le même ancien solde. |
| Durabilité | Une fois que la base de données a confirmé un commit, la modification survit à une panne ou à une coupure de courant. | Quand l’application affiche « Virement effectué », l’argent a vraiment été transféré, car la modification est déjà dans le journal sur disque. |
import sqlite3
conn = sqlite3.connect("bank.db")
# The accounts table was created with CHECK (balance >= 0)
def transfer(from_id, to_id, amount):
try:
with conn: # one transaction: commit on success, rollback on error
debit = conn.execute(
"UPDATE accounts SET balance = balance - ? WHERE id = ?",
(amount, from_id))
credit = conn.execute(
"UPDATE accounts SET balance = balance + ? WHERE id = ?",
(amount, to_id))
if debit.rowcount != 1 or credit.rowcount != 1:
raise ValueError("No such account") # forces a rollback
return "Transfer complete"
except (sqlite3.Error, ValueError):
return "Transfer refused: no account was changed"Remarquez la vérification de rowcount (le nombre de lignes modifiées par chaque mise à jour) : une mise à jour qui ne trouve aucun compte n’est pas une erreur pour la base de données, donc le code doit lui-même la traiter comme telle. Deux autres détails comptent au-delà de cet exemple. Les marqueurs ? transmettent les valeurs séparément du texte SQL, ce qui est la défense standard contre l’injection SQL, une attaque où une saisie de l’utilisateur est glissée dans une requête sous forme de code (le chapitre 8 y revient). Et la promesse de durabilité est tenue grâce à un journal d’écriture anticipée (write-ahead log) : le SGBD enregistre chaque modification dans un journal sur disque, où l’on ne fait qu’ajouter, avant de confirmer le commit, de sorte qu’après une panne il peut rejouer ou annuler du travail pour revenir à un état correct.
L’isolation est la propriété la plus subtile. L’isolation complète (dite sérialisable) est sûre mais limite la quantité de travail qui peut s’exécuter en parallèle ; les bases de données proposent donc des niveaux d’isolation plus faibles qui échangent une partie de la protection contre de la vitesse, et beaucoup utilisent par défaut un niveau plus faible. Les anomalies que chaque niveau autorise sont traitées dans transactions et isolation. Pour l’instant, l’idée clé est qu’une base de données unique offre des garanties fortes et bien comprises à l’intérieur d’une transaction, et que ces garanties sont bien plus difficiles à obtenir une fois les données réparties entre plusieurs bases de données ou services.
NoSQL : d’autres formes de données
Les bases de données relationnelles ne sont pas la seule option. NoSQL est un nom générique, popularisé lors d’une rencontre à San Francisco en 2009, pour les bases de données qui n’utilisent pas le modèle relationnel. Elles se sont développées pour traiter de très gros volumes répartis sur de nombreuses machines, des formes de données souples et des modes d’accès que les tables servent mal. Le nom dit ce qu’elles ne sont pas, et il couvre donc des familles très différentes.
Les quatre grandes familles NoSQL
| Famille | Comment elle stocke les données | Bien adaptée à | Exemples |
|---|---|---|---|
| Base orientée documents | Chaque enregistrement est un document autonome, en général proche du JSON, qui peut imbriquer des listes et des sous-objets. Les documents d’une même collection n’ont pas besoin d’avoir exactement les mêmes champs. | Les données lues et écrites d’un seul bloc : un catalogue de produits aux attributs variés, un profil utilisateur, le contenu d’une page web. | MongoDB, Couchbase, Firestore |
| Base clé-valeur | Un dictionnaire géant : une clé unique correspond à une valeur dont la base de données ne regarde pas le contenu. La recherche se fait uniquement par la clé. | Les recherches simples et très rapides : sessions, paniers d’achat, caches, feature flags. | Redis, Amazon DynamoDB, etcd |
| Base orientée colonnes larges | Des lignes identifiées par une clé, chacune avec de nombreuses colonnes, regroupées et réparties (partitionnées) sur de nombreuses machines. Les tables sont conçues autour des requêtes qui seront exécutées. | D’énormes volumes d’écritures, comme des relevés de capteurs, des journaux d’événements et des historiques de messagerie, sur de nombreux serveurs et centres de données. | Apache Cassandra, Apache HBase, Google Bigtable |
| Base orientée graphe | Des nœuds (les choses) et des arêtes (les relations), les uns comme les autres avec des propriétés, comme dans les graphes de la section 0.2. | Les questions sur les liens : amis d’amis, réseaux de fraude, calcul d’itinéraires, recommandations. | Neo4j, Amazon Neptune |
Les spécialistes ne sont pas d’accord sur le choix par défaut. Un camp conseille de commencer par une base de données relationnelle, sauf raison claire de faire autrement : SQL est très connu, les transactions et les jointures sont puissantes, et les systèmes relationnels modernes stockent aussi très bien les documents JSON. L’autre camp conseille de choisir dès le départ le stockage adapté au mode d’accès, car une base orientée documents ou clé-valeur peut être plus simple et passer à l’échelle à moindre coût pour la bonne charge de travail. Les deux s’accordent sur le fait que beaucoup de systèmes réels utilisent plus d’un type de base, chacun là où il convient : c’est ce qu’on appelle la persistance polyglotte. La section du chapitre 7 consacrée au choix d’une base de données donne une méthode structurée pour décider.
Exemple commenté : le schéma d’une boutique en ligne
Prenons une petite boutique en ligne. Elle doit savoir qui sont ses clients, ce qu’elle vend et qui a commandé quoi. Une première tentative pourrait tout mettre dans une seule table, avec une ligne par commande et des colonnes product1, product2, product3. Cela ne marche plus dès que quelqu’un commande quatre articles, et cela recopie le nom et l’adresse du client dans chaque commande.
Exemple commenté
Exemple commenté : pourquoi le schéma a quatre tables
Le schéma ci-dessus répond aux questions de la boutique sans recopier de faits.
| customers | Une ligne par personne. Changer une adresse e-mail modifie une seule ligne. |
|---|---|
| orders | Une ligne par passage au paiement. customer_id est une clé étrangère, donc chaque commande appartient à un vrai client. Un client a de nombreuses commandes : c’est une relation un-à-plusieurs. |
| products | Une ligne par article en vente, avec le prix actuel et le niveau de stock. |
| order_items | Une commande contient de nombreux produits et un produit figure dans de nombreuses commandes : c’est une relation plusieurs-à-plusieurs. Les bases de données relationnelles la représentent par une table de liaison, avec une ligne par produit et par commande. |
| Copie délibérée : unit_price | Le prix est recopié dans order_items au moment où la commande est passée. Ce n’est pas une erreur de normalisation : la commande doit garder la trace de ce que le client a réellement payé, même si le prix du catalogue change la semaine suivante. |
| Passer une commande | L’insertion de la commande, l’insertion de ses articles et la baisse du stock de chaque produit se font dans une seule transaction, si bien qu’une panne ne peut jamais laisser une commande sans articles, ni un stock qui n’a pas été diminué. |
Une bonne conception de schéma part des questions que se pose l’entreprise et des faits qu’elle ne doit jamais perdre, pas des écrans de la première version de l’application.
La même boutique, dans une base orientée documents, pourrait stocker chaque commande comme un seul document contenant ses articles imbriqués, ce qui convient pour lire une commande entière d’un coup, mais complique des questions comme « total des ventes par produit ».
Sauvegardes, restauration et bases de données managées
Les disques tombent en panne, des personnes lancent le mauvais DELETE, les logiciels ont des bugs et des attaquants chiffrent les données pour obtenir une rançon. Une sauvegarde est une copie des données conservée dans un endroit séparé, à partir de laquelle la base de données peut être restaurée. Trois habitudes professionnelles comptent. Une sauvegarde qui n’a jamais été restaurée est un espoir, pas une sauvegarde : les équipes s’exercent donc à restaurer et chronomètrent l’opération. Les copies sont conservées à plus d’un endroit (une règle empirique courante : trois copies, sur deux types de supports, dont une hors site). Et une réplique, une seconde base de données qui copie la première en continu, protège contre la panne d’une machine, mais pas contre les erreurs : un mauvais DELETE atteint la réplique en quelques millisecondes. Beaucoup de bases de données peuvent combiner une sauvegarde complète périodique et le journal des transactions conservé pour revenir à n’importe quel moment : c’est la restauration à un instant donné (point-in-time recovery).
Bien exploiter une base de données (appliquer les correctifs, l’optimiser, la répliquer, la sauvegarder et la restaurer à trois heures du matin) est un travail spécialisé et sans fin ; les fournisseurs de cloud vendent donc des bases de données managées : Amazon RDS, Azure SQL Database et Google Cloud SQL en sont des exemples. Le fournisseur s’occupe des machines, des mises à jour logicielles, des sauvegardes automatiques et du basculement (failover) vers une copie de secours ; le client reste responsable du schéma, des requêtes, des index, des droits d’accès et du coût. Les services managés sont traités plus largement dans serveurs et cloud.
Pourquoi c’est important pour l’architecture
Les données survivent en général au code qui les entoure : les applications sont réécrites tous les quelques ans, alors que les dossiers clients et les historiques médicaux sont conservés pendant des décennies. Les décisions sur l’endroit où se trouvent les données, le type de base qui les contient, le programme qui en est propriétaire et les garanties dont elles ont besoin comptent donc parmi les plus coûteuses à remettre en cause. Plusieurs grandes questions d’architecture découlent directement de cette section : chaque service doit-il posséder sa propre base de données (propriété des données), quelle base de données convient à quel usage (choisir une base de données), et comment garder des données correctes quand une seule action métier touche plusieurs bases de données, là où une transaction ACID unique n’est plus possible (sagas). Quand un architecte demande « où se trouve la source de vérité pour ceci ? », c’est à cause de cette section.
Vérifiez votre compréhension
Pourquoi un fichier de tableur est-il un mauvais endroit pour garder les commandes d’une boutique en ligne dès que de nombreux clients achètent en même temps ?
Des écritures simultanées peuvent s’écraser les unes les autres, une panne en cours d’enregistrement peut corrompre le fichier, trouver les commandes d’un client oblige à tout lire, et rien n’impose de règles comme le fait que chaque commande ait un vrai client. Un SGBD résout ces quatre problèmes.
Dans le schéma de la boutique, qu’est-ce qui fait de customer_id dans la table orders une clé étrangère, et qu’est-ce que cela vous apporte ?
Elle contient la clé primaire d’une ligne de la table customers et elle est déclarée comme une référence à celle-ci. La base de données refuse alors les commandes pour des clients qui n’existent pas.
Pourquoi ne pas simplement indexer toutes les colonnes de toutes les tables ?
Chaque index doit être mis à jour à chaque insertion, mise à jour et suppression, et il occupe de l’espace. Trop d’index rendent les écritures lentes et le stockage coûteux ; on choisit donc les index en fonction des requêtes réellement exécutées.
Quelle propriété ACID est violée si une panne laisse Alice débitée sans que Bob soit crédité ?
L’atomicité : les deux modifications auraient dû avoir lieu ensemble, ou pas du tout.
Une équipe déclare : « Nous avons une réplique dans un autre centre de données, donc nous n’avons pas besoin de sauvegardes. » Qu’est-ce qui lui a échappé ?
Une réplique copie les erreurs et les données corrompues aussi fidèlement que les bonnes données ; une suppression accidentelle ou une attaque par rançongiciel l’atteint donc en quelques instants. Les sauvegardes, idéalement avec restauration à un instant donné, protègent contre cela.
Sources et lectures complémentaires
Une question sur cette section ? ArchitectBot, l’assistant d’étude IA du cours, répond d’abord à partir du cours.
Demander : « Pour une petite boutique en ligne, comment choisir entre une base de données relationnelle comme PostgreSQL et une base orientée documents comme MongoDB ? »