3.4. Transactions

Les transactions sont un concept fondamental de tous les systèmes de bases de données. Le point essentiel d'une transaction est qu'il assemble plusieurs étapes en une seule, une opération tout-ou-rien. Les états intermédiaires entre les étapes ne sont pas visibles pour les autres transactions concurrentes, et si un échec survient empêchant la fin réussie de la transaction, alors aucune des étapes n'affecte la base de données.

Par exemple, considérez la base de données d'une banque qui contiendrait la balance pour différents comptes clients, ainsi que les balances du total du dépôt par branches. Supposez que nous voulons enregistrer un virement de 100 euros du compte d'Alice vers celui de Bob. En simplifiant énormément, les commandes SQL pour ceci ressembleraient à ça

UPDATE comptes SET balance = balance - 100.00
    WHERE nom = 'Alice';
UPDATE branches SET balance = balance - 100.00
    WHERE nom = (SELECT nom_branche FROM comptes WHERE nom = 'Alice');
UPDATE comptes SET balance = balance + 100.00
    WHERE nom = 'Bob';
UPDATE branches SET balance = balance + 100.00
    WHERE nom = (SELECT nom_branche FROM comptes WHERE nom = 'Bob');

Les détails de ces commandes ne sont pas importants ici ; le point important est que cela nécessite plusieurs mises à jour séparées pour accomplir cette opération assez simple. Les officiels de la banque voudront être assurés que soit toutes les commandes sont effectuées soit aucune ne l'ait. Il ne serait pas acceptable que, suite à une erreur du système, Bob reçoive 100 euros qui n'ont pas été débités du compte d'Alice. De la même façon, Alice ne restera pas longtemps une cliente si elle est débitée du montant sans que celui-ci ne soit crédité sur le compte de Bob. Nous avons besoin d'une garantie comme quoi si quelque chose se passe mal, aucune des étapes déjà exécutées ne prendra effet. Grouper les mises à jour en une transaction nous donne cette garantie. Une transaction est dite atomique : du point de vue des autres transactions, cela se passe complètement ou pas du tout.

Nous voulons aussi la garantie qu'une fois une transaction terminée et validée par le système de base de données, les modifications seront enregistrées de façon permanente et ne seront pas perdues même si un arrêt brutal arrive peu après. Par exemple, si nous enregistrons un retrait d'argent par Bob, nous ne voulons surtout pas que le débit de son compte disparaisse lors d'un crash à sa sortie de la banque. Une base de données transactionnelle garantie que toutes les mises à jour faites lors d'une transaction sont enregistrées dans un stockage permanent (c'est-à-dire sur disque) avant que la transaction ne soit validée.

Une autre propriété importante des bases de données transactionnelles est en relation étroite avec la notion de mises à jour atomiques : quand de multiples transactions sont lancées en parallèle, chacune d'entre elles ne doit pas être capable de voir les modifications incomplètes faites par les autres. Par exemple, si une transaction est occupée en faisant le total de toutes les branches, il ne serait pas bon d'inclure le débit de la branche d'Alice mais sans le crédit de la branche de Bob, ou vice-versa. Donc, les transactions doivent être tout-ou-rien non seulement pour leur effet permanent sur la base de données, mais aussi pour leur visibilité au moment de leur exécution. Les mises à jour faites ainsi par une transaction ouverte sont invisibles aux autres transactions jusqu'à la fin de celle-ci, moment qui rendra visible toutes les mises à jours simultanément.

Avec PostgreSQL, une transaction est réalisée en entourant les commandes SQL de la transaction avec les commandes BEGIN et COMMIT. Donc, notre transaction pour la banque ressemblera à ceci

BEGIN;
UPDATE comptes SET balance = balance - 100.00
    WHERE nom = 'Alice';
-- etc etc
COMMIT;

Si, au cours de la transaction, nous décidons que nous ne voulons pas valider (peut-être nous sommes-nous aperçu que la balance d'Alice devenait négative), nous pouvons envoyer la commande ROLLBACK au lieu de COMMIT, et toutes nos mises à jour jusqu'à maintenant seront annulées.

En fait, PostgreSQL traite chaque instruction SQL comme étant exécutée dans une transaction. Si vous ne lancez pas une commande BEGIN, alors chaque instruction individuelle se trouve enveloppée avec un BEGIN et (en cas de succès) un COMMIT implicites. Un groupe d'instructions entouré par un BEGIN et un COMMIT est quelque fois appelé un bloc transactionnel.

Note : Quelques bibliothèques clients lancent les commandes BEGIN et COMMIT automatiquement, de façon à ce que vous bénéficiiez des effets des blocs transactionnels sans les demander. Vérifiez la documentation de l'interface que vous utilisez.