Comprenez comment les garanties ACID influent sur la conception des bases de données et le comportement des applications. Explorez atomicité, cohérence, niveaux d'isolation, durabilité, compromis et exemples concrets.

Quand vous payez vos courses, réservez un vol ou effectuez un virement, vous attendez un résultat sans équivoque : soit ça a marché, soit ça n'a pas marché. Les bases de données cherchent à offrir la même certitude — même lorsque de nombreuses personnes utilisent le système en même temps, que des serveurs plantent ou que le réseau flanche.
Une transaction est une unité de travail que la base traite comme un seul « paquet ». Elle peut inclure plusieurs étapes — soustraire de l'inventaire, créer une commande, débiter une carte et écrire un reçu — mais elle doit se comporter comme une action cohérente.
Si une étape échoue, le système doit revenir à un point sûr plutôt que de laisser un travail à moitié fini.
Les mises à jour partielles ne sont pas de simples bugs techniques ; elles deviennent des tickets au support et un risque financier. Par exemple :
Ces échecs sont difficiles à diagnostiquer parce que tout semble « en grande partie correct », mais les chiffres ne collent pas.
ACID est l'abréviation de quatre garanties que beaucoup de bases de données peuvent fournir pour les transactions :
Ce n'est pas une marque de base de données ni un seul réglage à activer ; c'est une promesse de comportement.
Des garanties plus fortes signifient généralement que la base doit faire plus de travail : coordination supplémentaire, attente de verrous, suivi de versions et écriture dans des journaux. Cela peut réduire le débit ou augmenter la latence sous forte charge. L'objectif n'est pas « le maximum d'ACID en permanence », mais de choisir les garanties qui correspondent à vos risques métier réels.
L'atomicité signifie qu'une transaction est traitée comme une unité de travail : elle se termine complètement ou n'a aucun effet. Vous n'obtenez jamais une « mise à jour à moitié visible » dans la base.
Imaginez transférer 50 $ d'Alice à Bob. En coulisses, cela implique généralement au moins deux changements :
Avec l'atomicité, ces deux changements réussissent ensemble ou échouent ensemble. Si le système ne peut pas faire les deux en toute sécurité, il ne doit en faire aucun. Cela évite le cauchemar où Alice est débitée mais Bob ne reçoit pas l'argent (ou l'inverse).
Les bases de données offrent deux sorties pour une transaction :
Un modèle mental utile est « brouillon vs publication ». Tant que la transaction s'exécute, les changements sont provisoires. Seul un commit les publie.
L'atomicité est cruciale parce que les pannes sont normales :
Si l'un de ces événements survient avant la fin du commit, l'atomicité permet à la base de faire un rollback pour que le travail partiel ne s'infiltre pas dans les soldes réels.
L'atomicité protège l'état de la base, mais votre application doit quand même gérer l'incertitude — surtout quand une perte réseau rend incertain le fait qu'un commit ait eu lieu.
Deux compléments pratiques :
Ensemble, transactions atomiques et reprises idempotentes vous aident à éviter à la fois les mises à jour partielles et les doubles prélèvements accidentels.
La cohérence dans ACID ne signifie pas « les données ont l'air raisonnables » ou « toutes les réplicas correspondent ». Cela signifie que chaque transaction doit faire passer la base d'un état valide à un autre état valide — selon les règles que vous définissez.
Une base de données ne peut assurer la cohérence que par rapport à des contraintes explicites, des triggers et des invariants qui décrivent ce que « valide » signifie pour votre système. ACID n'invente pas ces règles ; il les applique pendant les transactions.
Exemples courants :
order.customer_id doit pointer vers un client existant.Si ces règles sont en place, la base rejettera toute transaction qui les violerait — vous n'obtiendrez donc pas de données « à moitié valides ».
La validation dans l'app est importante, mais pas suffisante :
Un mode de défaillance classique : vérifier dans l'app que l'email est disponible, puis insérer la ligne. Sous concurrence, deux requêtes peuvent réussir la vérification simultanément. Une contrainte d'unicité en base garantit qu'une seule insertion réussira.
Si vous encodez « pas de soldes négatifs » comme contrainte (ou l'appliquez de façon fiable dans une seule transaction), alors tout transfert qui mettrait le compte dans le rouge doit échouer dans son ensemble. Si vous n'encodiez cette règle nulle part, ACID ne peut pas la protéger — car il n'y a rien à faire appliquer.
La cohérence revient finalement à être explicite : définissez les règles, puis laissez les transactions garantir qu'elles ne sont jamais brisées.
L'isolation garantit que les transactions ne se marchent pas sur les pieds. Pendant qu'une transaction est en cours, les autres ne devraient pas voir de travail à moitié fini ni l'écraser par inadvertance. L'objectif est simple : chaque transaction doit se comporter comme si elle tournait seule, même quand de nombreux utilisateurs sont actifs en même temps.
Les systèmes réels sont chargés : des clients passent des commandes, des agents support modifient des profils, des jobs en arrière-plan rapprochent des paiements — le tout en même temps. Ces actions se chevauchent et touchent souvent les mêmes lignes (un solde, un compteur d'inventaire, un créneau de réservation).
Sans isolation, le timing devient une partie de votre logique métier. Une mise à jour « soustraire du stock » peut faire course avec un autre paiement, ou un rapport peut lire des données en plein changement et afficher des chiffres qui n'ont jamais existé dans un état stable.
L'isolation « totale » (se comporter comme si vous étiez seul) peut être coûteuse. Elle peut réduire le débit, augmenter l'attente (verrous) ou provoquer des reprises de transactions. Parallèlement, de nombreux workflows n'ont pas besoin de la protection la plus stricte — la lecture des analytics d'hier, par exemple, peut tolérer des incohérences mineures.
C'est pourquoi les bases offrent des niveaux d'isolation configurables : vous choisissez le risque de concurrence acceptable en échange d'une meilleure performance et de moins de conflits.
Quand l'isolation est trop faible pour votre charge, vous rencontrerez des anomalies classiques :
Comprendre ces modes d'échec facilite le choix d'un niveau d'isolation qui corresponde aux promesses de votre produit.
L'isolation détermine ce que vous êtes autorisé à « voir » pendant que d'autres transactions s'exécutent. Quand elle est trop faible, vous pouvez obtenir des anomalies — des comportements techniquement possibles mais surprenants pour les utilisateurs.
Une lecture sale se produit quand vous lisez des données écrites par une autre transaction mais non commitée.
Scénario : Alex transfère 500 $ hors d'un compte, le solde devient temporairement 200 $, et vous lisez ces 200 $ avant que le transfert d'Alex n'échoue et soit annulé.
Conséquence utilisateur : un client voit un solde incorrectement bas, une règle antifraude se déclenche à tort, ou un agent support donne une mauvaise réponse.
Une lecture non répétable signifie que vous lisez deux fois la même ligne et obtenez des valeurs différentes parce qu'une autre transaction a commit entre-temps.
Scénario : vous chargez le total d'une commande (49,00 $), puis rafraîchissez et voyez 54,00 $ parce qu'une ligne de remise a été supprimée.
Conséquence utilisateur : « mon total a changé pendant que je finalisais ma commande », ce qui mène à de la défiance ou à l'abandon du panier.
Un phantom read ressemble à une lecture non répétable, mais sur un ensemble de lignes : une seconde requête retourne des lignes en plus (ou en moins) car une autre transaction a inséré/supprimé des enregistrements correspondants.
Scénario : une recherche d'hôtel affiche « 3 chambres disponibles », puis lors de la réservation le système re-vérifie et n'en trouve aucune parce que de nouvelles réservations ont été ajoutées.
Conséquence utilisateur : tentatives de double réservation, écrans de disponibilité incohérents ou survente d'inventaire.
Un lost update survient lorsque deux transactions lisent la même valeur et écrivent des mises à jour ; la dernière écriture écrase la précédente.
Scénario : deux admins modifient le même prix produit. Tous deux partent de 10 $ ; l'un enregistre 12 $, l'autre 11 $ en dernier.
Conséquence utilisateur : la modification de quelqu'un disparaît ; totaux et rapports erronés.
Un write skew se produit lorsque deux transactions effectuent chacune un changement valide individuellement, mais ensemble elles violent une règle.
Scénario : règle : « au moins un médecin de garde doit être planifié ». Deux médecins se déclarent chacun indisponibles après avoir vérifié que l'autre est toujours de garde.
Conséquence utilisateur : se retrouver sans couverture, malgré des transactions qui « passent » individuellement.
Une isolation plus forte réduit les anomalies mais peut augmenter l'attente, les reprises et les coûts sous haute concurrence. Beaucoup de systèmes choisissent une isolation plus faible pour les lectures analytiques, tout en utilisant des réglages plus stricts pour les transferts d'argent, les réservations et autres flux critiques pour la cohérence.
L'isolation traite de ce que votre transaction est autorisée à « voir » pendant que d'autres s'exécutent. Les bases l'exposent via des niveaux d'isolation : les niveaux supérieurs réduisent les comportements surprenants, mais coûtent en débit ou en attentes.
Les équipes choisissent souvent Read Committed par défaut pour les apps orientées utilisateur : bonne performance et l'absence de lectures sales correspond à la plupart des attentes.
Utilisez Repeatable Read quand vous avez besoin de résultats stables dans une transaction (par ex. générer une facture) et que vous pouvez tolérer un certain coût. Utilisez Serializable quand l'exactitude prime sur la concurrence (par ex. empêcher la survente), ou quand il est difficile de raisonner sur les courses dans le code applicatif.
Read Uncommitted est rare en OLTP ; il sert parfois pour du monitoring ou des rapports approximatifs où des lectures erronées occasionnelles sont acceptables.
Les noms sont standardisés, mais les garanties exactes diffèrent selon le moteur de base de données (et parfois selon la configuration). Consultez la documentation de votre SGBD et testez les anomalies qui importent pour votre business.
La durabilité signifie que, une fois qu'une transaction est committée, ses résultats doivent survivre à un crash — coupure de courant, redémarrage de processus ou reboot inattendu de la machine. Si votre app dit à un client « paiement réussi », la durabilité garantit que la base ne va pas « oublier » ce fait après la prochaine panne.
La plupart des bases relationnelles atteignent la durabilité via le write-ahead logging (WAL). De façon simplifiée, la base écrit un « reçu » séquentiel des changements dans un journal sur disque avant de considérer la transaction comme committée. Si la base plante, elle peut rejouer le journal au démarrage pour restaurer les changements commités.
Pour garder les temps de récupération raisonnables, les bases créent aussi des checkpoints. Un checkpoint est un moment où la base s'assure qu'assez de changements récents sont écrits dans les fichiers de données principaux, pour que la récupération n'ait pas à rejouer un historique de journal infini.
La durabilité n'est pas un interrupteur on/off ; elle dépend de l'agressivité avec laquelle la base force les données vers un stockage stable :
fsync du système) avant de confirmer le commit. C'est plus sûr, mais peut ajouter de la latence.Le matériel sous-jacent compte aussi : SSD, contrôleurs RAID avec cache d'écriture et volumes cloud peuvent se comporter différemment en cas de défaillance.
Les sauvegardes et la réplication vous aident à récupérer ou réduire le downtime, mais elles ne sont pas identiques à la durabilité. Une transaction peut être durable sur le primaire même si elle n'a pas encore atteint une réplique, et les sauvegardes sont typiquement des snapshots à un instant donné plutôt que des garanties commit-par-commit.
Quand vous BEGIN une transaction puis COMMIT, la base coordonne de nombreux éléments : qui peut lire quelles lignes, qui peut les mettre à jour, et ce qui se passe si deux personnes essaient de modifier le même enregistrement en même temps.
Un choix clé est la manière de gérer les conflits :
Beaucoup de systèmes mélangent les deux selon la charge et le niveau d'isolation.
Les bases modernes utilisent souvent MVCC (Multi-Version Concurrency Control) : plutôt que de garder une seule copie d'une ligne, la base conserve plusieurs versions.
C'est une grande raison pour laquelle certaines bases gèrent beaucoup de lectures et d'écritures en parallèle avec moins de blocage — bien que les conflits écriture/écriture nécessitent toujours une résolution.
Les verrous peuvent mener à des deadlocks : la transaction A attend un verrou détenu par B, tandis que B attend un verrou détenu par A.
Les bases détectent typiquement le cycle et abortent une transaction (la « victime » du deadlock), renvoyant une erreur pour que l'application puisse réessayer.
Si l'application subit des frottements liés à l'application d'ACID, vous verrez souvent :
Ces symptômes indiquent souvent qu'il est temps de revoir la taille des transactions, l'indexation ou la stratégie d'isolation/verrouillage adaptée à la charge.
Les garanties ACID ne sont pas que de la théorie ; elles influencent la conception des API, des jobs en arrière-plan et même des flux UI. L'idée centrale est simple : décidez quelles étapes doivent réussir ensemble, puis encapsulez uniquement ces étapes dans une transaction.
Une API transactionnelle bien conçue mappe généralement à une seule action métier, même si elle touche plusieurs tables. Par exemple, une opération /checkout pourrait : créer une commande, réserver l'inventaire et enregistrer une intention de paiement. Ces écritures en base devraient généralement vivre dans une même transaction pour qu'elles commitent ensemble (ou rollbackent ensemble) si une validation échoue.
Un schéma courant :
Cela conserve atomicité et cohérence tout en évitant des transactions longues et fragiles.
Où vous placez les limites de transaction dépend de ce que signifie « une unité de travail » :
ACID aide, mais l'app doit toujours gérer les erreurs correctement :
Évitez les transactions longues, appeler des API externes dans une transaction et le temps de réflexion de l'utilisateur dans une transaction (par ex. « verrouiller la ligne du panier, demander la confirmation à l'utilisateur »). Ces pratiques augmentent la contention et rendent les conflits d'isolation bien plus probables.
Si vous construisez un système transactionnel rapidement, le risque majeur n'est généralement pas « ne pas connaître ACID » — c'est disperser accidentellement une action métier sur plusieurs endpoints, jobs ou tables sans frontière transactionnelle claire.
Des plateformes comme Koder.ai peuvent vous aider à aller plus vite tout en concevant autour d'ACID : vous pouvez décrire un workflow (par ex. « checkout avec réservation d'inventaire et intention de paiement ») dans un chat orienté planning, générer une UI React plus un backend Go + PostgreSQL, et itérer avec snapshots/rollback si un schéma ou une frontière transactionnelle doit changer. La base continue d'appliquer les garanties ; la valeur additionnelle est d'accélérer le chemin d'une conception correcte à une implémentation fonctionnelle.
Une base unique peut généralement livrer des garanties ACID à l'intérieur d'une même transaction. Une fois que vous répartissez le travail sur plusieurs services (et souvent plusieurs bases), ces mêmes garanties deviennent plus difficiles à maintenir — et plus coûteuses si vous essayez.
La cohérence stricte signifie que chaque lecture voit la « dernière vérité commitée ». La haute disponibilité signifie que le système continue de répondre même quand des parties sont lentes ou inaccessibles.
Dans une architecture multiservice, un problème réseau temporaire peut forcer un choix : bloquer ou échouer les requêtes jusqu'à ce que tous les participants s'accordent (plus cohérent, moins disponible), ou accepter que des services soient brièvement en décalage (plus disponible, moins cohérent). Rien n'est « toujours juste » — tout dépend des erreurs que votre métier peut tolérer.
Les transactions distribuées exigent une coordination au-delà de frontières que vous ne contrôlez pas complètement : latences réseau, retries, timeouts, crashs de services et pannes partielles.
Même si chaque service est correct, le réseau peut créer de l'ambiguïté : le service de paiement a‑t‑il commit tandis que le service de commande n'a jamais reçu l'accusé ? Pour résoudre cela en toute sécurité, on utilise des protocoles de coordination (comme two‑phase commit), qui peuvent être lents, réduire la disponibilité en cas de panne et ajouter de la complexité opérationnelle.
Sagas découpent un workflow en étapes, chacune commitée localement. Si une étape ultérieure échoue, les étapes antérieures sont « annulées » par des actions compensatoires (par ex. rembourser une charge).
Pattern outbox/inbox rend la publication d'événements et leur consommation fiable. Un service écrit les données métier et un enregistrement « événement à publier » dans la même transaction locale (outbox). Les consommateurs enregistrent les IDs de message traités (inbox) pour gérer les retries sans dupliquer les effets.
Cohérence éventuelle accepte de courts décalages entre services, avec un plan clair de réconciliation.
Relaxez les garanties lorsque :
Contrôlez le risque en définissant des invariants (ce qui ne doit jamais être violé), en concevant des opérations idempotentes, en utilisant timeouts et retries avec backoff, et en surveillant la dérive (sagas bloquées, compensations répétées, tables outbox qui gonflent). Pour des invariants vraiment critiques (par ex. « ne jamais dépasser un compte »), conservez-les au sein d'un seul service et d'une seule transaction de base si possible.
Une transaction peut être « correcte » en test unitaire et quand même échouer sous trafic réel, redémarrages et concurrence. Utilisez cette checklist pour aligner les garanties ACID sur le comportement en production.
Commencez par écrire ce qui doit toujours être vrai (vos invariants de données). Exemples : « le solde ne devient jamais négatif », « le total d'une commande égale la somme des lignes », « l'inventaire ne peut pas passer sous zéro », « un paiement est lié à exactement une commande ». Traitez-les comme des règles produit, pas de simples détails de base.
Puis décidez ce qui doit être à l'intérieur d'une transaction versus ce qui peut être différé.
Gardez les transactions petites : touchez moins de lignes, faites moins de travail (pas d'appels externes), et commitez rapidement.
Faites de la concurrence une dimension de test à part entière.
Si vous supportez les retries, ajoutez une clé d'idempotence explicite et testez « requête répétée après succès ».
Surveillez des indicateurs montrant que vos garanties deviennent coûteuses ou fragiles :
Alertez sur les tendances, pas seulement sur les pics, et reliez les métriques aux endpoints ou jobs qui les causent.
Utilisez l'isolation la plus faible qui protège encore vos invariants ; n'augmentez pas par défaut au maximum. Quand vous avez besoin d'exactitude stricte pour une petite section critique (mouvement d'argent, décrément d'inventaire), restreignez la transaction à juste cette section et placez le reste en dehors.
ACID est un ensemble de garanties transactionnelles qui aident les bases de données à se comporter de façon prévisible en cas de pannes et de concurrence :
Une transaction est une « unité de travail » que la base de données traite comme un seul paquet. Même si elle exécute plusieurs instructions SQL (par exemple : créer une commande, décrémenter l'inventaire, enregistrer une intention de paiement), elle n'a que deux issues :
Parce que les mises à jour partielles créent des contradictions concrètes et coûteuses à corriger, par exemple :
ACID (notamment atomicité + cohérence) empêche que ces états « à moitié terminés » deviennent la vérité visible.
L'atomicité garantit que la base de données n'expose jamais une transaction « à moitié terminée ». Si quelque chose échoue avant le commit — crash de l'app, perte réseau, redémarrage de la BDD — la transaction est annulée pour que les étapes précédentes ne se propagent pas dans l'état persistant.
En pratique, l'atomicité rend sûres les opérations multi-étapes (par exemple un virement qui met à jour deux soldes).
On ne peut pas toujours savoir si un commit a eu lieu si le client perd la réponse (par exemple timeout réseau juste après le commit). Combinez les transactions ACID avec :
Cela évite à la fois les mises à jour partielles et les doubles prélèvements/écritures accidentels.
Dans ACID, « cohérence » signifie que la base de données passe d'un état valide à un autre état valide selon des règles que vous définissez — contraintes, clés étrangères, unicité, checks.
Si vous n'encodiez pas une règle (par ex. « le solde ne peut pas être négatif »), ACID ne pourra pas l'appliquer automatiquement. La base a besoin d'invariants explicites pour protéger cette propriété.
La validation côté application améliore l'expérience utilisateur et peut appliquer des règles métier complexes, mais elle peut échouer sous concurrence (deux requêtes peuvent valider la même condition en même temps).
Les contraintes en base agissent comme le dernier rempart :
Utilisez les deux : validez tôt dans l'app, faites respecter définitivement en base.
L'isolation contrôle ce que votre transaction peut observer pendant que d'autres s'exécutent. Une isolation faible peut produire des anomalies telles que :
Une base pratique par défaut est Read Committed pour beaucoup d'applications OLTP : empêche les lectures sales et offre de bonnes performances. Montez en niveau quand c'est nécessaire :
Confirmez toujours le comportement dans votre engine : les détails varient.
La durabilité garantit que, une fois qu'une base de données confirme un commit, le changement survivra aux plantages. C'est généralement implémenté via le write-ahead logging (WAL) et des checkpoints.
Ce qui peut l'affaiblir :
Les backups et la réplication aident la récupération et la disponibilité, mais ne sont pas identiques à la garantie de durabilité.
Les niveaux d'isolation vous permettent d'équilibrer performance et protection contre ces anomalies.