Leer hoe ACID-garanties databaseontwerp en applicatiegedrag beïnvloeden. Ontdek atomiciteit, consistentie, isolatie, duurzaamheid, afwegingen en praktische voorbeelden.

Als je boodschappen betaalt, een vlucht boekt of geld tussen rekeningen verplaatst, verwacht je een eenduidig resultaat: of het is gelukt, of het is niet gelukt. Databases proberen diezelfde zekerheid te bieden—ook als veel mensen tegelijk het systeem gebruiken, servers crashen of netwerken haperen.
Een transactie is een enkele eenheid werk die de database als één "pakket" behandelt. Het kan meerdere stappen omvatten—voorraad verminderen, een orderrecord aanmaken, een kaart afschrijven en een ontvangstbewijs wegschrijven—maar het hoort zich als één samenhangende handeling te gedragen.
Als een stap faalt, moet het systeem terugdraaien naar een veilige toestand in plaats van een half afgemaakt rommeltje achter te laten.
Gedeeltelijke updates zijn niet alleen technische storingen; ze worden klantondersteuningstickets en financieel risico. Bijvoorbeeld:
Deze fouten zijn moeilijk te debuggen omdat alles er "grotendeels goed" uitziet, terwijl de cijfers niet kloppen.
ACID is een afkorting voor vier garanties die veel databases kunnen bieden voor transacties:
Het is geen specifieke databasebrand of een enkele schakelaar; het is een belofte over gedrag.
Sterkere garanties betekenen meestal dat de database meer werk moet doen: extra coördinatie, wachten op locks, versies bijhouden en naar logs schrijven. Dat kan doorvoer verminderen of latentie verhogen onder zware belasting. Het doel is niet "maximale ACID altijd", maar garanties kiezen die passen bij je reële bedrijfsrisico's.
Atomiciteit betekent dat een transactie als één eenheid werk wordt behandeld: of alles voltooid wordt, of er geen effect is. Je eindigt nooit met een "halve update" zichtbaar in de database.
Stel je voor dat je $50 van Alice naar Bob overmaakt. Achter de schermen zijn dat meestal minstens twee wijzigingen:
Met atomiciteit slagen die twee wijzigingen samen of ze falen samen. Als het systeem niet beide veilig kan doen, moet het geen van beide doen. Dat voorkomt het nachtmerriescenario waarin Alice wordt gefactureerd maar Bob het geld niet ontvangt (of Bob ontvangt het zonder dat Alice is gefactureerd).
Databases geven transacties twee uitwegen:
Een nuttig mentaal model is "concept vs. publiceren". Terwijl de transactie loopt, zijn de wijzigingen voorlopig. Alleen een commit publiceert ze.
Atomiciteit is belangrijk omdat fouten normaal zijn:
Als dit gebeurt vóór voltooiing van de commit, zorgt atomiciteit ervoor dat de database kan terugdraaien zodat gedeeltelijk werk niet in echte saldi lekt.
Atomiciteit beschermt de databasetoestand, maar je applicatie moet nog steeds omgaan met onzekerheid—vooral wanneer een netwerkonderbreking onduidelijk maakt of een commit heeft plaatsgevonden.
Twee praktische aanvullingen:
Samen helpen atomische transacties en idempotente retries om zowel gedeeltelijke updates als per ongeluk dubbele afschrijvingen te voorkomen.
Consistentie in ACID betekent niet "de data ziet er redelijk uit" of "alle replica's komen overeen". Het betekent dat elke transactie de database van één geldige staat naar een andere geldige staat brengt—volgens de regels die jij definieert.
Een database kan data alleen consistent houden ten opzichte van expliciete constraints, triggers en invarianties die beschrijven wat "geldig" betekent voor jouw systeem. ACID verzint deze regels niet; het handhaaft ze tijdens transacties.
Veelvoorkomende voorbeelden:
order.customer_id moet verwijzen naar een bestaande klant.Als deze regels aanwezig zijn, zal de database elke transactie die ze zou schenden weigeren—zodat je niet met "half‑geldige" data eindigt.
Validatie in de app is belangrijk, maar op zichzelf niet voldoende.
Een klassiek falen is: de app controleert "email is beschikbaar" en voegt daarna de rij in. Onder concurrentie kunnen twee requests tegelijkertijd de check passeren. Een unique constraint in de database garandeert dat slechts één insert slaagt.
Als je "geen negatieve saldi" als constraint vastlegt (of het betrouwbaar afdwingt binnen één transactie), moet elke overboeking die een rekening overtrekt als geheel falen. Als je die regel nergens vastlegt, kan ACID het niet beschermen—omdat er niets is om te handhaven.
Consistentie gaat uiteindelijk over expliciet zijn: definieer de regels en laat transacties ervoor zorgen dat ze nooit worden gebroken.
Isolatie zorgt ervoor dat transacties elkaar niet in de weg zitten. Terwijl een transactie bezig is, mogen andere transacties geen half‑afgewerkt werk zien of het per ongeluk overschrijven. Het doel is eenvoudig: elke transactie moet zich gedragen alsof hij alleen draait, zelfs als veel gebruikers tegelijk actief zijn.
Echte systemen zijn druk: klanten plaatsen bestellingen, support‑medewerkers werken profielen bij, achtergrondjobs vereffenen betalingen—allemaal tegelijk. Deze acties overlappen in de tijd en raken vaak dezelfde rijen (een rekening‑saldo, voorraadcount of boekingsslot).
Zonder isolatie wordt timing onderdeel van je bedrijfslogica. Een "voorraad aftrekken" update kan racen met een andere checkout, of een rapport kan tijdens een wijziging lezen en cijfers tonen die nooit in een stabiele staat bestonden.
Volledige "gedraag je alsof je alleen bent" isolatie kan duur zijn. Het kan doorvoer verminderen, wachten op locks vergroten of transacties vaker laten herhalen. Tegelijk hebben veel workflows niet de strengste bescherming nodig—het lezen van de analyses van gisteren kan bijvoorbeeld kleine inconsistenties verdragen.
Daarom bieden databases configureerbare isolatieniveaus: je kiest hoeveel concurrency‑risico je accepteert in ruil voor betere prestaties en minder conflicten.
Als isolatie te zwak is voor je workload, kom je klassieke anomalieën tegen:
Begrijpen welke foutmodi relevant zijn maakt het makkelijker om een isolatieniveau te kiezen dat past bij je productbeloften.
Isolatie bepaalt welke andere transacties je mag "zien" terwijl de jouwe nog draait. Als isolatie te zwak is voor een workload, krijg je anomalieën—technisch mogelijk gedrag dat gebruikers verbaast.
Dirty read ontstaat als je data leest die een andere transactie heeft geschreven maar nog niet heeft gecommit.
Scenario: Alex boekt $500 van een rekening over, het saldo wordt tijdelijk $200, en jij leest dat $200 voordat Alex’ transactie later faalt en wordt teruggedraaid.
Gebruikersuitkomst: een klant ziet een onjuist laag saldo, een frauderegel slaat aan of een supportmedewerker geeft het verkeerde antwoord.
Non‑repeatable read betekent dat je twee keer dezelfde rij leest en verschillende waarden krijgt omdat een andere transactie intussen heeft gecommit.
Scenario: Je laadt een ordertotaal ($49,00), ververs het even later en ziet $54,00 omdat een kortingsregel is verwijderd.
Gebruikersuitkomst: "Mijn totaal veranderde tijdens het afrekenen", wat wantrouwen of afgebroken aankopen kan geven.
Phantom read lijkt op non‑repeatable read, maar dan voor een set rijen: een tweede query retourneert extra (of ontbrekende) rijen doordat een andere transactie overeenkomende records heeft ingevoegd/verwijderd.
Scenario: Een hotelzoekopdracht toont "3 kamers beschikbaar", bij het boeken controleert het systeem opnieuw en vindt geen kamers meer omdat er nieuwe reserveringen zijn bijgekomen.
Gebruikersuitkomst: dubbele boekingspogingen, inconsistente beschikbaarheidsschermen of overselling.
Lost update doet zich voor wanneer twee transacties dezelfde waarde lezen en beiden schrijven, waarbij de latere write de eerdere overschrijft.
Scenario: Twee admins bewerken dezelfde prijs. Beiden beginnen bij $10; de één slaat $12 op, de ander slaat als laatste $11 op.
Gebruikersuitkomst: iemands wijziging verdwijnt; totalen en rapporten kloppen niet.
Write skew gebeurt als twee transacties elk een wijziging doorvoeren die op zichzelf geldig is, maar samen een regel schenden.
Scenario: Regel: "Er moet ten minste één dienstdoende dokter zijn." Twee dokters zetten zich onafhankelijk af door te controleren dat de ander nog op de lijst staat.
Gebruikersuitkomst: er blijft niemand over, ondanks dat elke transactie afzonderlijk de checks passeerde.
Sterkere isolatie vermindert anomalieën maar kan wachten, retries en kosten verhogen bij hoge concurrentie. Veel systemen kiezen zwakkere isolatie voor read‑zware analytics, en strengere instellingen voor geldbewegingen, boekingen en andere correctheidskritische flows.
Isolatie gaat over wat je transactie mag "zien" terwijl anderen draaien. Dat exposeert zich in databases als isolatieniveaus: hogere niveaus verminderen verrassend gedrag maar kunnen doorvoer of wachttijd kosten.
Teams kiezen vaak Read Committed als standaard voor gebruikersapps: goede prestaties en het voorkomen van dirty reads dekt de meeste verwachtingen.
Gebruik Repeatable Read wanneer je stabiele resultaten binnen een transactie nodig hebt (bijv. factuurgeneratie) en je enige overhead kunt verdragen.
Gebruik Serializable wanneer correctheid belangrijker is dan concurrency (bijv. het afdwingen van complexe invarianties zoals "nooit oversell"), of wanneer je racecondities in applicatiecode niet eenvoudig kunt redeneren.
Read Uncommitted is zeldzaam in OLTP‑systemen; soms gebruikt voor monitoring of ruwe rapportage waar af en toe verkeerde reads acceptabel zijn.
Namen zijn gestandaardiseerd, maar exacte garanties verschillen per database‑engine (en soms per configuratie). Controleer de documentatie van je database en test de anomalieën die relevant zijn voor je bedrijf.
Duurzaamheid betekent dat zodra een transactie gecommit is, de resultaten een crash moeten overleven—stroomuitval, procesherstart of een plotselinge reboot. Als je app tegen een klant zegt "betaling geslaagd", is duurzaamheid de belofte dat de database dat feit niet zal "vergeten" na de volgende storing.
De meeste relationele databases realiseren duurzaamheid met write‑ahead logging (WAL). Op hoofdlijnen schrijft de database een sequentiële "kwitantie" van wijzigingen naar een log op schijf voordat de transactie als gecommit wordt beschouwd. Als de database crasht, kan hij de log tijdens het opstarten afspelen om de gecommitte wijzigingen te herstellen.
Om hersteltijd redelijk te houden, maken databases ook checkpoints. Een checkpoint is een moment waarop de database ervoor zorgt dat genoeg recente wijzigingen naar de hoofddatafiles zijn geschreven, zodat recovery geen onbegrensde hoeveelheid log hoeft af te spelen.
Duurzaamheid is geen enkele aan/uit‑schakelaar; het hangt af van hoe agressief de database data naar stabiele opslag forceert.
Ook de onderliggende hardware telt mee: SSD's, RAID‑controllers met write‑caches en cloudvolumes gedragen zich verschillend bij fouten.
Backups en replicatie helpen je herstellen of downtime verminderen, maar ze zijn niet hetzelfde als duurzaamheid. Een transactie kan duurzaam zijn op de primaire zelfs als hij nog niet bij een replica is aangekomen, en backups zijn meestal point‑in‑time snapshots in plaats van commit‑per‑commit garanties.
Als je BEGIN en later COMMIT uitvoert, coördineert de database veel bewegende delen: wie welke rijen mag lezen, wie ze mag bijwerken en wat er gebeurt als twee mensen hetzelfde record willen veranderen.
Een belangrijke onderliggende keuze is hoe je conflicten afhandelt:
Veel systemen mengen beide ideeën afhankelijk van workload en isolatieniveau.
Moderne databases gebruiken vaak MVCC (Multi‑Version Concurrency Control): in plaats van één kopie van een rij bij te houden, bewaart de database meerdere versies.
Dit verklaart grotendeels waarom sommige databases veel reads en writes gelijktijdig kunnen verwerken met minder blokkering—hoewel write/write‑conflicten nog steeds opgelost moeten worden.
Locks kunnen leiden tot deadlocks: Transactie A wacht op een lock van B, terwijl B wacht op een lock van A.
Databases lossen dit doorgaans op door de cyclus te detecteren en één transactie af te breken (een "deadlock victim"), waarbij een fout wordt geretourneerd zodat de applicatie kan retryen.
Als ACID‑handhaving wrijving veroorzaakt, zie je vaak:
Deze symptomen betekenen vaak dat het tijd is om transactieomvang, indexering of isolatie/vergrendelingsstrategie te herzien.
ACID‑garanties zijn geen database‑theorie alleen—ze beïnvloeden hoe je API's, achtergrondjobs en zelfs UI‑flows ontwerpt. Het kernidee is simpel: beslis welke stappen samen moeten slagen en wikkel alleen die stappen in een transactie.
Een goede transactionele API mapt meestal naar één zakelijke actie, ook al raakt die meerdere tabellen. Bijvoorbeeld een /checkout operatie kan: een order aanmaken, voorraad reserveren en een payment intent registreren. Die database‑writes horen meestal in één transactie zodat ze samen committeren (of samen terugdraaien) als er een validatie faalt.
Een veelgebruikt patroon is:
Dit houdt atomiciteit en consistentie terwijl je trage, fragiele transacties vermijdt.
Waar je transactiegrenzen trekt hangt af van wat "één eenheid werk" betekent:
ACID helpt, maar je applicatie moet fouten nog steeds correct afhandelen:
Vermijd lange transacties, externe API‑calls binnen een transactie en gebruikerstijd binnen een transactie (bijv. "lock cart rij, vraag gebruiker om bevestiging"). Deze vergroten contentie en maken isolatieconflicten veel waarschijnlijker.
Als je snel een transactioneel systeem bouwt, is het grootste risico zelden "ACID niet kennen"—het is per ongeluk één bedrijfsactie over meerdere endpoints, jobs of tabellen verspreiden zonder duidelijke transactiegrens.
Platforms zoals Koder.ai kunnen helpen je sneller vooruit te helpen terwijl je nog steeds rond ACID ontwerpt: je beschrijft een workflow (bijv. "checkout met voorraadreservering en payment intent"), genereert een React UI plus een Go + PostgreSQL backend en iterereert met snapshots/rollback als een schema of transactiegrens moet veranderen. De database handhaaft nog steeds de garanties; de waarde zit in het versnellen van het pad van correct ontwerp naar werkende implementatie.
Een enkele database kan meestal ACID‑garanties leveren binnen één transactierand. Zodra je werk verspreidt over meerdere services (en vaak meerdere databases), worden die garanties moeilijker te behouden—en kostbaarder als je ze probeert af te dwingen.
Strikte consistentie betekent dat elke read de "laatste gecommitte waarheid" ziet. Hoge beschikbaarheid betekent dat het systeem blijft antwoorden, zelfs als onderdelen traag of onbereikbaar zijn.
In een multi‑service setup kan een tijdelijk netwerkprobleem een keuze afdwingen: verzoeken blokkeren of falen totdat elke deelnemer het eens is (meer consistent, minder beschikbaar), of accepteren dat services kortdurend uit sync zijn (meer beschikbaar, minder consistent). Geen van beide is altijd juist—het hangt af van welke fouten je bedrijf kan tolereren.
Gedistrubueerde transacties vereisen coördinatie over grenzen die je niet volledig beheerst: netwerkvertragingen, retries, timeouts, servicecrashes en gedeeltelijke fouten.
Zelfs als elke service correct is, kan het netwerk ambiguïteit creëren: heeft de payment service gecommit maar ontving de order service nooit de bevestiging? Om dat veilig op te lossen gebruiken systemen coördinatieprotocollen (zoals two‑phase commit), die traag kunnen zijn, beschikbaarheid kunnen verminderen tijdens fouten en operationele complexiteit toevoegen.
Sagas breken een workflow in stappen, elk lokaal gecommit. Als een latere stap faalt, worden eerdere stappen "ongedaan" gemaakt met compenserende acties (bijv. een terugbetaling).
Outbox/inbox patronen maken event publicatie en consumptie betrouwbaar. Een service schrijft zowel bedrijfsdata als een "event to publish" record in dezelfde lokale transactie (outbox). Consumenten slaan verwerkte message IDs op (inbox) om retries zonder duplicatie af te handelen.
Eventual consistency accepteert korte vensters waarin data tussen services verschilt, met een plan voor reconciliatie.
Versoepel garanties wanneer:
Beperk risico door invarianties te definiëren (wat nooit mag worden geschonden), idempotente operaties te ontwerpen, timeouts en retries met backoff te gebruiken en drift te monitoren (vastgelopen sagas, herhaalde compensaties, groeiende outbox‑tabellen). Voor echt kritische invarianties (bijv. "nooit een tekort op een rekening") houd je ze binnen één service en één database‑transactie waar mogelijk.
Een transactie kan in een unittest "correct" zijn en toch falen onder echt verkeer, herstarts en concurrency. Gebruik deze checklist om ACID‑garanties af te stemmen op hoe je systeem zich in productie gedraagt.
Begin met opschrijven wat altijd waar moet zijn (je data‑invarianties). Voorbeelden: "rekening‑saldo nooit negatief", "ordertotaal is som van de lijnitems", "voorraad kan niet onder nul", "een betaling is gelinkt aan precies één order." Zie dit als productregels, niet als database‑trivia.
Bepaal vervolgens wat binnen één transactie moet vallen en wat uitgesteld kan worden.
Houd transacties klein: raak minder rijen aan, doe minder werk (geen externe API‑calls) en commit snel.
Maak concurrency een eersteklas testdimensie.
Als je retries ondersteunt, voeg een expliciete idempotency key toe en test "verzoek herhaald na succes".
Houd indicatoren in de gaten die aantonen dat je garanties duur of fragiel worden:
Alert op trends, niet alleen op spikes, en koppel metrics terug naar endpoints of jobs die ze veroorzaken.
Gebruik de zwakste isolatie die je invarianties beschermt; zet het niet standaard op maximaal. Wanneer je strikte correctheid nodig hebt voor een klein, cruciaal stuk (geldbeweging, voorraaddecrement), beperk de transactie tot precies dat stuk en houd alles anders buiten de transactie.
ACID is een reeks transactionele garanties die databases helpen voorspelbaar te blijven bij fouten en concurrentie:
Een transactie is een enkele "unit of work" die de database als één pakket behandelt. Zelfs als het meerdere SQL‑statements uitvoert (bijv. order aanmaken, voorraad verminderen, payment intent vastleggen), heeft het maar twee uitkomsten:
Deels bijgewerkte gegevens creëren reële tegenstrijdigheden die later duur zijn te herstellen, bijvoorbeeld:
ACID (vooral atomiciteit + consistentie) voorkomt dat deze "half-af" toestanden als waarheid zichtbaar worden.
Atomiciteit zorgt ervoor dat de database nooit een "half‑voltooide" transactie blootstelt. Als er iets misgaat vóór de commit—app crash, netwerkonderbreking, DB‑restart—wordt de transactie teruggedraaid zodat eerdere stappen niet in de persistente staat lekken.
In de praktijk maakt atomiciteit multi‑staps wijzigingen (zoals een overboeking die twee balances bijwerkt) veilig.
Je kunt niet altijd weten of een commit heeft plaatsgevonden als de client de respons verliest (bijv. netwerk time-out direct na commit). Combineer ACID met:
Dit voorkomt zowel gedeeltelijke updates als per ongeluk dubbele afschrijvingen of dubbele writes.
In ACID betekent "consistentie" dat de database van één geldige toestand naar een andere geldige toestand gaat volgens de regels die jij definieert—constraints, foreign keys, unique checks en dergelijke.
Als je een regel niet ergens vastlegt (bijv. "balance mag niet negatief worden"), kan ACID die regel niet automatisch afdwingen. De database heeft expliciete invarianties nodig om te beschermen.
Validatie in de applicatie verbetert de gebruikerservaring en kan complexe regels afdwingen, maar faalt soms bij concurrency (twee requests kunnen tegelijk de check passeren).
Database‑constraints zijn de laatste poortwachter:
Gebruik beide: valideer vroeg in de app, handhaaf definitief in de database.
Isolatie bepaalt wat je transactie kan zien terwijl anderen draaien. Zwakke isolatie kan anomalieën veroorzaken zoals:
Isolatieniveaus laten je prestatie ruilen tegen bescherming tegen deze anomalieën.
Een praktisch basisniveau is Read Committed voor veel OLTP‑apps omdat het dirty reads voorkomt en goede prestaties biedt. Ga hoger wanneer nodig:
Controleer altijd het gedrag in jouw database‑engine; details verschillen.
Duurzaamheid betekent dat zodra de database een commit bevestigt, die wijziging crashes overleeft. Dit wordt meestal gerealiseerd met write‑ahead logging (WAL) en checkpoints.
Let op configuratiekeuzes:
Backups en replicatie helpen herstel en beschikbaarheid, maar zijn geen synoniem voor duurzaamheid.