Les transactions SQL : ACID, COMMIT, ROLLBACK
Imaginez un virement bancaire : débiter le compte A de 100 €, puis créditer le compte B de 100 €. Si le système tombe en panne entre ces deux opérations, l’argent disparaît — débité d’un côté, jamais arrivé de l’autre. La transaction SQL existe pour rendre ce scénario impossible : elle regroupe plusieurs instructions en un bloc indivisible qui réussit entièrement ou échoue entièrement, sans état intermédiaire.
Ce guide couvre le principe ACID, les commandes BEGIN, COMMIT et ROLLBACK, les savepoints, les niveaux d’isolation et les verrous, avec des exemples concrets tout du long.
Le principe ACID
Une transaction respecte quatre propriétés résumées par l’acronyme ACID. Chacune répond à un risque précis.
Atomicité
L’atomicité garantit que la transaction est un tout : ou bien toutes ses opérations sont appliquées, ou bien aucune. Il n’existe pas de « moitié de transaction ». Dans notre virement, impossible que le débit ait lieu sans le crédit.
Cohérence
La cohérence assure que la base passe d’un état valide à un autre état valide. Les contraintes (clés étrangères, CHECK, unicité) sont respectées à la fin de la transaction. Si une opération violerait une règle, l’ensemble est annulé.
Isolation
L’isolation garantit que deux transactions simultanées ne se marchent pas dessus. Chacune s’exécute comme si elle était seule, même quand des dizaines tournent en parallèle. Le degré d’isolation est réglable, comme nous le verrons.
Durabilité
La durabilité assure qu’une fois la transaction validée (COMMIT), ses effets sont permanents — inscrits sur disque et résistants à une coupure de courant immédiatement après.
BEGIN, COMMIT, ROLLBACK
Trois commandes structurent toute transaction. On ouvre avec BEGIN (ou START TRANSACTION), on valide avec COMMIT, on annule avec ROLLBACK.
BEGIN;
UPDATE comptes SET solde = solde - 100 WHERE id = 'A';
UPDATE comptes SET solde = solde + 100 WHERE id = 'B';
COMMIT;
Tant que le COMMIT n’est pas exécuté, les modifications restent invisibles pour les autres connexions et réversibles. Si un problème survient, ROLLBACK annule tout ce qui a été fait depuis le BEGIN.
BEGIN;
UPDATE comptes SET solde = solde - 100 WHERE id = 'A';
-- Contrôle applicatif : le solde est devenu négatif ?
-- On décide d'annuler
ROLLBACK;
-- Le compte A retrouve son solde initial, rien n'est enregistré
Selon les moteurs, la syntaxe varie légèrement : MySQL utilise START TRANSACTION, PostgreSQL accepte BEGIN comme START TRANSACTION, et l’auto-commit (chaque instruction validée seule) est le comportement par défaut hors transaction explicite.
L’exemple du transfert bancaire
Reprenons le virement de bout en bout, avec le contrôle métier qui justifie l’existence de la transaction.
CREATE TABLE comptes (
id TEXT PRIMARY KEY,
solde NUMERIC NOT NULL CHECK (solde >= 0)
);
INSERT INTO comptes VALUES ('A', 500), ('B', 200);
BEGIN;
UPDATE comptes SET solde = solde - 100 WHERE id = 'A';
UPDATE comptes SET solde = solde + 100 WHERE id = 'B';
COMMIT;
La contrainte CHECK (solde >= 0) est ici la gardienne de la cohérence. Si le compte A n’avait que 50 €, le premier UPDATE violerait la contrainte, déclencherait une erreur, et l’ensemble serait annulé — laissant les deux comptes intacts. Sans transaction, le crédit du compte B aurait pu passer même après l’échec du débit.
Les savepoints : annuler partiellement
Un savepoint est un point de reprise à l’intérieur d’une transaction. Il permet d’annuler seulement une partie des opérations sans tout perdre. Utile dans une transaction longue où certaines étapes sont facultatives.
BEGIN;
INSERT INTO commandes (id, client_id) VALUES (1, 42);
SAVEPOINT avant_remise;
UPDATE commandes SET remise = 10 WHERE id = 1;
-- Finalement, on renonce à la remise
ROLLBACK TO SAVEPOINT avant_remise;
-- L'insertion de la commande est conservée
COMMIT;
Le ROLLBACK TO SAVEPOINT annule tout ce qui suit le savepoint, mais garde le reste. On peut libérer un savepoint devenu inutile avec RELEASE SAVEPOINT avant_remise. Cette granularité est précieuse dans les traitements par lots.
Les niveaux d’isolation
L’isolation n’est pas tout ou rien : elle se règle par niveaux, du plus permissif au plus strict. Le compromis se fait entre exactitude et performance — plus l’isolation est forte, plus les transactions se bloquent mutuellement.
-- Régler le niveau pour la transaction en cours
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
Le standard SQL définit quatre niveaux, chacun autorisant ou interdisant certaines anomalies.
READ UNCOMMITTED
Le plus permissif. Une transaction peut lire des données modifiées par une autre transaction non encore validée : c’est la lecture sale (dirty read). Rarement souhaitable, il est même traité comme READ COMMITTED par PostgreSQL.
READ COMMITTED
On ne lit que des données validées, ce qui élimine les lectures sales. C’est le niveau par défaut de PostgreSQL et d’Oracle. Reste possible la lecture non répétable : relire la même ligne dans la transaction peut donner une valeur différente si une autre transaction l’a modifiée entre-temps.
REPEATABLE READ
Une même ligne relue dans la transaction renvoie toujours la même valeur. C’est le niveau par défaut de MySQL (InnoDB). Il empêche les lectures non répétables ; selon les moteurs, il peut encore laisser passer les lectures fantômes (de nouvelles lignes apparaissant dans un intervalle).
SERIALIZABLE
Le plus strict. Les transactions se comportent comme si elles s’exécutaient l’une après l’autre, en série. Toutes les anomalies sont écartées, au prix de plus de blocages et d’éventuelles erreurs de sérialisation à gérer côté application.
Le tableau des anomalies évitées :
Niveau | Lecture sale | Non répétable | Fantôme
------------------+--------------+---------------+--------
READ UNCOMMITTED | possible | possible | possible
READ COMMITTED | évitée | possible | possible
REPEATABLE READ | évitée | évitée | possible*
SERIALIZABLE | évitée | évitée | évitée
(*PostgreSQL évite aussi les fantômes en REPEATABLE READ.)
Les verrous
Pour appliquer l’isolation, la base pose des verrous (locks) sur les lignes ou les tables. Un verrou empêche une autre transaction de modifier — voire de lire — une donnée tant qu’il est tenu.
Vous pouvez verrouiller explicitement des lignes que vous comptez modifier, afin d’éviter qu’une transaction concurrente ne les change entre votre lecture et votre écriture.
BEGIN;
-- Verrouille les lignes lues jusqu'au COMMIT
SELECT solde FROM comptes WHERE id = 'A' FOR UPDATE;
UPDATE comptes SET solde = solde - 100 WHERE id = 'A';
COMMIT;
Le SELECT ... FOR UPDATE réserve les lignes : toute autre transaction voulant les modifier attendra la fin de celle-ci. C’est le remède classique aux conditions de course sur un solde ou un stock.
L’interblocage (deadlock)
Deux transactions peuvent se bloquer mutuellement : la première tient un verrou qu’attend la seconde, et inversement. C’est un interblocage (deadlock). Les bases le détectent et sacrifient l’une des transactions, qui reçoit une erreur et doit être relancée. Pour limiter le risque, acquérez toujours les verrous dans le même ordre dans toute l’application (par exemple, verrouiller toujours le compte au plus petit identifiant en premier).
Bonnes pratiques
- Gardez les transactions courtes. Une transaction ouverte longtemps retient ses verrous et bloque les autres. Faites vos calculs avant, ouvrez la transaction au dernier moment.
- Ne mettez pas d’appels réseau dans une transaction. Attendre une API externe verrou en main est une source de blocages massifs.
- Gérez le rollback en cas d’erreur. Côté application, entourez la transaction d’un bloc de gestion d’exception qui appelle
ROLLBACKsi une opération échoue. - Choisissez le bon niveau d’isolation. Restez sur le niveau par défaut sauf besoin précis ; passez à
SERIALIZABLEpour les opérations financières sensibles, en acceptant de relancer sur erreur de sérialisation.
Les transactions vont souvent de pair avec des contraintes de cohérence entre tables ; pour bien poser ces règles, consultez notre article sur les clés étrangères et l’intégrité référentielle.
En résumé
Une transaction SQL regroupe des instructions en une unité atomique qui respecte les propriétés ACID. On l’ouvre avec BEGIN, on la valide avec COMMIT, on l’annule avec ROLLBACK, et les savepoints offrent une annulation partielle. Les niveaux d’isolation arbitrent entre exactitude et performance, tandis que les verrous et le SELECT ... FOR UPDATE protègent des accès concurrents. Gardez vos transactions courtes, gérez les erreurs par un rollback, et vos données resteront cohérentes même sous forte charge.