Transactions SQL et ACID expliquées : COMMIT, ROLLBACK et niveaux d’isolement
Que se passe-t-il si le serveur tombe en panne à mi-chemin du transfert d’argent entre deux comptes ? Les transactions sont la réponse. Découvrez comment ils fonctionnent sur un exemple réel, ce que signifie ACID et comment choisir un niveau d’isolement.
- Une transaction regroupe les instructions afin qu’elles réussissent toutes ensemble ou échouent toutes ensemble.
COMMITrend les modifications permanentes etROLLBACKles annule.- ACID signifie atomicité, cohérence, isolation et durabilité.
- Une isolation plus forte évite davantage d’anomalies, mais elle peut réduire la simultanéité, alors faites un choix délibéré.
Déplacer 300 roupies d’Asha vers Ben nécessite deux mises à jour : soustraire d’un compte et ajouter à l’autre. Si le système échoue entre eux, l’argent disparaît. Les transactions SQL existent pour rendre cela impossible, en traitant les deux étapes comme une seule unité.
Dans ce guide, nous passons en revue les validations, les restaurations et les échecs sur une petite table de comptes. Nous avons exécuté ces exemples dans SQLite et les soldes affichés proviennent de cette exécution. Les commandes de base sont similaires dans toutes les bases de données, mais le comportement de verrouillage et d’isolation varie beaucoup, c’est pourquoi nous vous indiquons où consulter la documentation de votre propre base de données.
Qu’est-ce qu’une transaction ?
Une transaction est une séquence d’une ou plusieurs instructions SQL que la base de données traite comme une opération logique unique. Voici notre tableau :
CREATE TABLE accounts (
id INT PRIMARY KEY,
owner TEXT,
balance INT CHECK (balance >= 0)
);
INSERT INTO accounts VALUES (1, 'Asha', 1000), (2, 'Ben', 200);
La contrainte CHECK est une règle selon laquelle un solde ne peut jamais descendre en dessous de zéro, que nous utiliserons plus tard. Notez que la syntaxe pour commencer une transaction varie : SQLite et PostgreSQL acceptent BEGIN, MySQL a également START TRANSACTION et SQL Server utilise BEGIN TRANSACTION.
En dehors d’une transaction explicite, la plupart des bases de données utilisent autocommit, où chaque instruction est enregistrée dès qu’elle se termine. Oracle est une exception notable, car une session y reste dans une transaction jusqu’à ce que vous vous engagez.
COMMIT : le rendre permanent
BEGIN;
UPDATE accounts SET balance = balance - 300 WHERE id = 1;
UPDATE accounts SET balance = balance + 300 WHERE id = 2;
COMMIT;
-- Asha | 700
-- Ben | 500
Jusqu’à l’exécution de COMMIT, les modifications sont provisoires. Une fois le retour réussi, la base de données garantit qu’ils sont enregistrés. Le total des deux comptes est resté à 1 200, ce qui est tout l’intérêt de l’exercice.
ROLLBACK : tout annuler
ROLLBACK annule tout depuis le début de la transaction :
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
SELECT owner, balance FROM accounts;
-- Asha | 600 (visible inside this transaction)
-- Ben | 500
ROLLBACK;
SELECT owner, balance FROM accounts;
-- Asha | 700 (back to where it was)
-- Ben | 500
Une restauration se produit également automatiquement si la connexion est interrompue ou si le serveur tombe en panne avant votre validation, de sorte qu’une transaction à moitié terminée ne survit pas.
Lorsqu’une instruction échoue
Voici un cas plus réaliste. Asha essaie d’en envoyer 5 000, que le compte ne détient pas. Le crédit à Ben s’exécute en premier, puis le débit enfreint la règle CHECK :
BEGIN;
UPDATE accounts SET balance = balance + 5000 WHERE id = 2;
UPDATE accounts SET balance = balance - 5000 WHERE id = 1;
-- error: CHECK constraint failed: balance >= 0
ROLLBACK;
-- Asha | 700
-- Ben | 500
Sans la transaction, Ben aurait conservé les 5 000 et les comptes seraient erronés. Soyez conscient d’un détail important : une instruction défaillante n’annule pas toujours la transaction par elle-même. Dans SQLite et MySQL, par exemple, les instructions antérieures peuvent rester en attente, votre code doit donc émettre la restauration. PostgreSQL marque à la place l’ensemble de la transaction comme abandonnée et refuse toute autre instruction jusqu’à ce que vous annuliez.
Dans le code de l’application, associez toujours la transaction à la gestion des erreurs. Voici l’idée en Python, et notre guide sur la gestion des exceptions Python explique la syntaxe :
try:
conn.execute("BEGIN")
conn.execute(debit_sql, values)
conn.execute(credit_sql, values)
conn.execute("COMMIT")
except Exception:
conn.execute("ROLLBACK")
raise
Points de sauvegarde
Un point de sauvegarde marque une position dans une transaction, vous ne pouvez donc annuler qu’une partie du travail :
BEGIN;
UPDATE accounts SET balance = balance - 50 WHERE id = 1;
SAVEPOINT bonus;
UPDATE accounts SET balance = balance + 999 WHERE id = 2;
ROLLBACK TO bonus;
UPDATE accounts SET balance = balance + 50 WHERE id = 2;
COMMIT;
-- Asha | 650
-- Ben | 550
Le bonus de 999 a été annulé, tandis que le reste de la transaction s’est poursuivi et engagé. Ceci est utile pour les étapes facultatives d’un long processus.
Propriétés ACIDE expliquées
| Propriété | Promesse | Dans notre transfert |
|---|---|---|
| Atomicité | Tout ou rien | Les deux mises à jour se produisent, ou ni l’une ni l’autre |
| Cohérence | Les règles sont toujours valables après | Aucun équilibre ne peut enfreindre la règle du CHECK |
| Isolement | Le travail simultané n’interfère pas | D’autres ne voient pas un transfert à moitié terminé |
| Durabilité | Les données validées survivent à l’échec | Une coupure de courant après COMMIT ne perd rien |
La cohérence est souvent mal comprise. La base de données applique uniquement les règles que vous déclarez, telles que les contraintes et les clés étrangères. Cela n’empêchera pas votre code de transférer le mauvais montant. La durabilité est généralement obtenue grâce à un journal à écriture anticipée, dans lequel les modifications sont enregistrées en toute sécurité sur le disque avant que la validation ne soit confirmée.
Que se passe-t-il avec les transactions simultanées ?
Nous avons ouvert deux connexions à un fichier SQLite. Le premier a démarré une transaction et mis à jour une ligne de 1 à 2, sans encore s’engager. Voici ce que la deuxième connexion a vu :
Connection B reads: 1 -- no uncommitted data
Connection B writes: database is locked -- it has to wait
(A commits)
Connection B reads: 2 -- now it sees the change
SQLite n’autorise qu’un seul rédacteur à la fois, ce qui est plus simple que les bases de données serveur, où de nombreuses transactions peuvent écrire différentes lignes simultanément. Mais le principe est le même : les autres sessions ne devraient pas voir les modifications non validées dans des paramètres normaux, et les écritures conflictuelles doivent attendre. Lors de notre test, nous avons configuré la deuxième connexion pour qu’elle abandonne immédiatement plutôt que d’attendre, de sorte que l’erreur est apparue immédiatement.
Lire les anomalies
Lorsque les transactions se chevauchent, certains problèmes peuvent survenir. Le standard SQL les définit en termes de ce qu’une transaction peut observer :
| Anomalie | Que se passe-t-il |
|---|---|
| Sale lecture | Vous lisez une modification qu’une autre transaction n’a pas validée et elle peut être annulée ultérieurement |
| Lecture non répétable | Vous lisez une ligne deux fois en une seule transaction et obtenez des valeurs différentes, car quelqu’un s’est engagé entre les deux |
| Lecture fantôme | Vous exécutez la même requête deux fois et obtenez des lignes supplémentaires ou manquantes, car quelqu’un en a inséré ou supprimé. |
Un autre problème est la mise à jour perdue. Deux sessions lisent un solde de 100, chacune ajoute 10 et chacune écrit 110. Une mise à jour a effectivement disparu, alors que la réponse devrait être 120.
Niveaux d’isolement SQL
Les niveaux d’isolement vous permettent d’échanger la rigueur contre la concurrence. Voici ce que le standard SQL autorise à chaque niveau :
| Niveau | Sale lecture | Lecture non répétable | Lecture fantôme |
|---|---|---|---|
| Lire non validé | Possible | Possible | Possible |
| Lire engagé | Empêché | Possible | Possible |
| Lecture répétable | Empêché | Empêché | Possible |
| Sérialisable | Empêché | Empêché | Empêché |
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
La syntaxe et le comportement exacts dépendent de votre base de données. Les valeurs par défaut diffèrent également : PostgreSQL et SQL Server utilisent la lecture validée, tandis que InnoDB de MySQL utilise la lecture répétable. Certaines bases de données offrent des garanties plus strictes que celles exigées par la norme ou traitent un niveau comme un autre. Dans PostgreSQL, par exemple, la lecture non validée se comporte comme la lecture validée. Lisez toujours la documentation de votre base de données avant de vous fier à un comportement spécifique.
Astuce : En cas de perte d’une mise à jour, effectuez la modification en une seule instruction, comme dans UPDATE accounts SET balance = balance + 10. Faire le calcul dans votre application à partir d’une lecture précédente est ce qui ouvre la brèche.
Impasses
Un blocage se produit lorsque deux transactions détiennent chacune un verrou dont l’autre a besoin. La transaction A verrouille la ligne 1 et veut la ligne 2, tandis que la transaction B verrouille la ligne 2 et veut la ligne 1. Aucune des deux ne peut continuer.
Les bases de données telles que PostgreSQL, MySQL et SQL Server le détectent et annulent une transaction avec une erreur. Il s’agit d’un comportement normal et votre application doit y être préparée. Pour réduire les blocages, mettez toujours à jour les lignes dans un ordre cohérent, par exemple en commençant par l’ID de compte le plus bas, gardez les transactions courtes et réessayez lorsque l’une d’entre elles est choisie comme victime.
Meilleures pratiques
- Gardez les transactions courtes. Les transactions longues détiennent des verrous et bloquent les autres utilisateurs.
- N’attendez jamais un utilisateur ou une API lente dans une transaction. Effectuez ce travail avant de commencer ou après.
- Toujours gérer les échecs. Chaque chemin doit se terminer par une validation ou une restauration.
- Ajouter des contraintes. Ils donnent à la base de données de vraies règles à appliquer. Une bonne conception de table est également utile.
- Utilisez de bons index. Des recherches plus rapides signifient que les verrous sont maintenus moins longtemps.
- Soyez prudent avec les nouvelles tentatives. Réessayez en cas de blocage, mais assurez-vous qu’une opération ne peut pas s’appliquer deux fois.
- Testez volontairement la simultanéité. Beaucoup de ces bugs n’apparaissent que sous charge, donc les tests d’intégration de base à eux seuls peuvent ne pas les révéler.
Pour conclure
Les transactions transforment plusieurs étapes risquées en une seule opération sûre, et ACID décrit les garanties qui rendent cette opération digne de confiance. Chaque fois qu’une action commerciale touche plus d’une ligne ou un tableau, demandez-vous ce qui se passerait si elle s’arrêtait à mi-chemin. Si la réponse vous inquiète, enveloppez-la dans une transaction. Pour vous entraîner, ouvrez deux sessions dans une base de données locale et essayez de produire vous-même les anomalies.
Questions fréquentes
Qu’est-ce qu’une transaction en SQL ?
Une transaction est un groupe d’instructions SQL traitées comme une seule unité. Soit ils sont tous enregistrés avec COMMIT, soit ils sont tous annulés avec ROLLBACK.
Que sont les propriétés ACID dans une base de données ?
ACID est synonyme d’atomicité, de cohérence, d’isolation et de durabilité. Ensemble, ils garantissent que les transactions se déroulent en tout ou rien, maintiennent les règles intactes, n’interfèrent pas et survivent aux échecs.
Quelle est la différence entre COMMIT et ROLLBACK ?
COMMIT enregistre définitivement toutes les modifications apportées à la transaction. ROLLBACK les annule et restitue les données telles qu’elles étaient avant le début de la transaction.
Qu’est-ce qu’une sale lecture ?
Une lecture sale se produit lorsqu’une transaction lit des modifications qu’une autre n’a pas encore validées. Si cette autre transaction est annulée, les données lues n’ont jamais vraiment existé.
Quel niveau d’isolement dois-je utiliser ?
Commencez par la valeur par défaut de votre base de données, ce qui convient généralement. Utilisez sérialisable pour la logique critique où les anomalies sont inacceptables et soyez prêt à réessayer les transactions ayant échoué.
Continuez à apprendre
Besoin d’aide pour les tests, WordPress ou le référencement ?
Nous aidons les équipes à proposer de meilleurs logiciels et à développer leurs sites. Dites-nous sur quoi vous travaillez et nous vous proposerons une prochaine étape claire.
Plus de guides sur tests, IA et développement moderne
Vous avez apprécié cet article ? Choisissez un sujet et accédez directement à nos nouveaux didacticiels.
Les hallucinations de l'IA expliquées : pourquoi les modèles de langage inventent les choses et comment les réduire
Les hallucinations de l'IA ont expliqué : ce qu'elles sont, pourquoi les modèles de langage les produisent, les types courants, comment les réduire …
Lire l'article →DevOpsVariables d'environnement expliquées : fichiers .env, secrets et configuration correctement effectués
Variables d'environnement expliquées avec des exemples Python que nous avons exécutés : lecture et définition, pourquoi chaque valeur est une chaîne…
Lire l'article →Développement logicielLimitation du débit de l'API expliquée : algorithmes, en-têtes et gestion des erreurs 429
Limitation du débit de l'API expliquée avec une simulation que nous avons exécutée : compartiment de jetons, fenêtre fixe, fenêtre glissante, 429 ré…
Lire l'article →Automatisation des testsSe moquer en Python : unittest.mock, patch et les erreurs qui font mentir les tests
Moquerie en Python avec unittest.mock, testée : Mock et return_value, side_effect, correctifs où un nom est utilisé, spec et autospec, dates moqueus…
Lire l'article →Tests logicielsCouverture du code expliquée : couverture des lignes par rapport aux branches et ce que signifie réellement 100 %
Couverture du code expliquée avec une véritable exécution de cover.py : couverture des lignes, des branches et des conditions, lecture d'un rapport,…
Lire l'article →Bases de données et SQLDELETE vs TRUNCATE vs DROP dans SQL : différences, restauration et quand les utiliser
DELETE vs TRUNCATE vs DROP expliqué avec SQL testé : ce que chacun supprime, restauration, déclencheurs, réinitialisation d'identité, vitesse et en …
Lire l'article →Bases de données et SQLVues SQL expliquées : créez, utilisez et évitez les pièges, avec des exemples
Vues SQL expliquées avec les requêtes que nous avons exécutées : création d'une vue, résultats en direct, masquage des colonnes, performances, vues …
Lire l'article →Développement logicielInterface TypeScript vs Type : différences réelles, testées avec le compilateur
Interface TypeScript vs type, vérifiée avec le vrai compilateur : ce que les deux peuvent faire, ce qu'un seul peut faire, la fusion des déclaration…
Lire l'article →Développement logicielPython *args et **kwargs expliqués : fonctions flexibles avec des exemples clairs
Python *args et **kwargs expliqués avec le code que nous avons exécuté : emballage et déballage, ordre des paramètres, arguments de mot-clé uniqueme…
Lire l'article →Développement logicielFonctions Python Lambda : syntaxe, utilisations réelles et quand ne pas les utiliser
Fonctions Python lambda expliquées avec le code que nous avons exécuté : syntaxe, tri avec clé, carte et filtre, fermetures, piège de liaison tardiv…
Lire l'article →Intelligence artificielleQue sont les agents IA ? Comment ils fonctionnent, avec une simple boucle d'agent que vous pouvez exécuter
Que sont les agents IA ? Un guide simple sur la boucle d'agent, les outils, la mémoire et les garde-fous, avec une petite boucle Python que nous avo…
Lire l'article →DevOpsSurveillance vs observabilité : la vraie différence, avec les journaux, les métriques et les traces
Surveillance et observabilité expliquées : échecs connus et inconnus, journaux, métriques et traces, présentation pas à pas des incidents de paiemen…
Lire l'article →DevOpsVolumes Docker vs montages Bind : conserver les données du conteneur de la bonne manière
Volumes Docker et montages de liaison expliqués : pourquoi les données du conteneur disparaissent, volumes nommés, montages de liaison, tmpfs, exemp…
Lire l'article →Automatisation des testsTDD vs BDD : principales différences, une fonctionnalité conçue dans les deux sens et quand les utiliser
TDD vs BDD expliqué avec une fonctionnalité de remise construite dans les deux sens : refactor rouge-vert avec le code que nous avons exécuté, scéna…
Lire l'article →Tests logicielsTests d'acceptation utilisateur (UAT) : processus, liste de contrôle et exemples réels
Les tests d'acceptation des utilisateurs sont expliqués : qui les exécute, en quoi ils diffèrent du contrôle qualité, un processus en sept étapes, u…
Lire l'article →Bases de données et SQLExplication des valeurs SQL NULL : IS NULL, COALESCE, NULLIF et les pièges à éviter
Les valeurs SQL NULL expliquées avec les requêtes que nous avons exécutées : logique à trois valeurs, IS NULL, COALESCE, NULLIF, comment les agrégat…
Lire l'article →Bases de données et SQLInstruction SQL CASE WHEN : syntaxe, exemples réels et erreurs courantes
SQL CASE WHEN expliqué avec les requêtes que nous avons exécutées : formulaires simples et recherchés, ordre des conditions, pivots avec SUM, astuce…
Lire l'article →Développement logicielQu'est-ce que JWT ? Les jetons Web JSON expliqués avec un exemple fonctionnel
Qu’est-ce que JWT ? JSON Web Tokens expliqué : les trois parties, un jeton construit et falsifié dans le code que nous avons exécuté, JWT vs session…
Lire l'article →Développement logicielBoucle d'événements JavaScript expliquée : pile d'appels, microtâches et minuteries avec des exemples
La boucle d'événements JavaScript expliquée avec le code que nous avons exécuté : pile d'appels, files d'attente de tâches et de microtâches, pourqu…
Lire l'article →Développement logicielGénérateurs Python et rendement expliqués : économisez de la mémoire avec des exemples réels
Les générateurs Python et le rendement expliqués avec le code que nous avons exécuté : comment ils s'arrêtent et reprennent, les économies de mémoir…
Lire l'article →Intelligence artificielleQue sont les intégrations ? Vecteurs et similarité cosinus expliqués simplement
Que sont les intégrations ? Un guide en langage simple sur les vecteurs, la similarité cosinus et la recherche sémantique, avec un petit exemple que…
Lire l'article →DevOpsDéploiement Bleu-Vert vs Canary : différences, compromis et comment choisir
Explication du déploiement bleu-vert ou canari : comment chacun fonctionne, coût, risque et restauration, regroupement d'utilisateurs stable que nou…
Lire l'article →Automatisation des testsTests instables : quelles sont leurs causes et comment les corriger définitivement
Des tests irréguliers expliqués avec des démos que nous avons exécutées : les principales causes, comment en confirmer et en diagnostiquer une, les …
Lire l'article →Tests logicielsTests de charge vs tests de contrainte : différences, métriques et exemples k6
Explication des tests de charge et des tests de stress : ce que chacun prouve, les tests de pointe et de trempage, les métriques qui comptent, pourq…
Lire l'article →Tests logicielsScénario de test et scénario de test : principales différences, exemples et quand les utiliser
Scénario de test et scénario de test expliqués : des définitions claires, un tableau côte à côte, une fonctionnalité de réinitialisation de mot de p…
Lire l'article →Bases de données et SQLOptimisation des requêtes SQL : lire les plans d'exécution et corriger les requêtes lentes, avec des délais
Optimisation des requêtes SQL testée sur un million de lignes : lire les plans d'exécution, ajouter les bons index, corriger les filtres non Sargabl…
Lire l'article →Bases de données et SQLSQL UNION vs UNION ALL : différences, exemples et performances testées
SQL UNION vs UNION ALL expliqué avec les requêtes que nous avons exécutées : des doublons, les règles suivent toutes les deux, un véritable bug de d…
Lire l'article →Développement logicielTutoriel API JavaScript Fetch : GET, POST, gestion des erreurs et délais d'attente
Un didacticiel de l'API JavaScript Fetch testé sur un serveur local : GET et POST, pourquoi 404 ne lance pas, un assistant réutilisable, des chaînes…
Lire l'article →Développement logicielGestion des fichiers Python : lecture, écriture, CSV, JSON et pathlib avec exemples
Gestion des fichiers Python expliquée avec le code que nous avons exécuté : modes ouverts, avec lecture de fichiers volumineux, CSV, JSON, pathlib, …
Lire l'article →Développement logicielClasses et objets Python : didacticiel POO avec des exemples réels
Classes et objets Python expliqués avec le code que nous avons exécuté : __init__, méthodes, attributs de classe par rapport à l'instance, héritage,…
Lire l'article →Intelligence artificielleQu'est-ce qu'un grand modèle de langage ? Comment fonctionnent les LLM, expliqués sans battage médiatique
Qu’est-ce qu’un grand modèle de langage ? Les LLM expliqués en langage simple : jetons, intégrations, attention, formation, fenêtres de température …
Lire l'article →DevOpsCommandes Linux pour les débutants : le guide du terminal essentiel avec des exemples
Commandes Linux pour débutants, regroupées par tâche : navigation, fichiers, affichage des journaux, grep, tuyaux, autorisations, processus et archi…
Lire l'article →Automatisation des testsSelenium vs Playwright : principales différences, code comparé et lequel choisir
Comparatif Selenium vs Playwright : architecture, attente, support du navigateur et du langage, débogage, même test dans les deux outils, conseils d…
Lire l'article →Tests logicielsPartitionnement d'équivalence : technique de conception de tests expliquée avec des exemples concrets
Le partitionnement d'équivalence expliqué : comment trouver des partitions valides et invalides, des exemples concrets d'âges, de mots de passe et d…
Lire l'article →Tests logicielsGravité vs priorité dans les tests logiciels : différences, matrice et exemples réels
Explication de la gravité et de la priorité dans les tests logiciels : des définitions claires, une matrice à quatre voies avec des exemples, des éc…
Lire l'article →Bases de données et SQLTutoriel sur les sous-requêtes SQL : types, sous-requêtes corrélées et EXISTE avec des exemples
Un didacticiel sur les sous-requêtes SQL avec les requêtes que nous avons exécutées : les sous-requêtes scalaires, IN, FROM et SELECT, les sous-requ…
Lire l'article →Bases de données et SQLTransactions SQL et ACID expliquées : COMMIT, ROLLBACK et niveaux d'isolement
Transactions SQL et ACID expliquées avec un virement bancaire que nous avons effectué : COMMIT, ROLLBACK, points de sauvegarde, verrous, niveaux d'i…
Lire l'article →Développement logicielGit merge vs rebase : différences, exemples et quand utiliser chacun
Git merge vs rebase expliqué avec de vraies sorties de log, un tableau comparatif, la règle d'or du rebase et un flux de travail sûr pour les équipes.
Lire l'article →Développement logicielList comprehension Python : syntaxe, exemples et cas à éviter
La list comprehension Python expliquée : syntaxe, filtres, if-else, boucles imbriquées, temps mesurés et cas où une boucle for classique est préféra…
Lire l'article →Bases de données et SQLJointures SQL expliquées avec exemples (Inner, Left, Right, Full)
Comprenez les jointures SQL pas à pas avec des tables et requêtes d'exemple : INNER, LEFT, RIGHT, FULL, CROSS et SELF, plus erreurs courantes et con…
Lire l'article →