I/D/E · Storage

ACID Transactions

Summary

The four guarantees that database transactions provide: Atomicity, Consistency, Isolation, and Durability—and how they enable reliable data operations

TL;DR

ACID is a set of guarantees that database transactions provide: Atomicity (all-or-nothing), Consistency (constraints preserved), Isolation (concurrent transactions don’t interfere), and Durability (committed = permanent). These guarantees simplify application development by letting you reason about operations as atomic units.

Visual Overview

ACID Properties
THE ACID GUARANTEES

                                                 
  A - ATOMICITY: All or Nothing                  
       
   BEGIN                                       
     Debit Account A: -$100                    
     Credit Account B: +$100     CRASH        
   COMMIT                                      
       
  If crash before COMMIT  Both ops rolled back  
  Money doesn't disappear                        
                                                 
  C - CONSISTENCY: Constraints Preserved         
       
   CHECK balance >= 0                          
   FOREIGN KEY user_id  users.id              
       
  Transaction can't leave DB in invalid state    
                                                 
  I - ISOLATION: No Interference                 
                             
    Tx A       Tx B    Running concurrently  
   Read X     Read X                         
   X = 10     X = 10   Both see consistent   
       snapshot              
                                                 
  D - DURABILITY: Committed = Permanent          
       
   COMMIT returns  Data on disk (WAL)         
   Server crash  Data survives restart        
       
                                                 


BANK TRANSFER EXAMPLE

                                                 
  Without ACID:                                  
  1. Debit A: balance = 900                     
  2.  CRASH                                
  3. Credit B: never happens                    
  Result: $100 vanished!                         
                                                 
  With ACID:                                     
  BEGIN TRANSACTION                              
  1. Debit A: balance = 900    (in memory)       
  2. Credit B: balance = 1100  (in memory)       
  3. Write to WAL                                
  4.  CRASH                                
  On restart: Check WAL, rollback uncommitted    
  Result: Both accounts unchanged!               
                                                 

The Four Properties

Atomicity: All or Nothing

A transaction is an indivisible unit—either all operations succeed, or all fail.

Example: Bank Transfer

BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 'A';
UPDATE accounts SET balance = balance + 100 WHERE id = 'B';
COMMIT;

If the system crashes after the first UPDATE but before COMMIT:

  • With atomicity: Both updates rolled back, money intact
  • Without atomicity: Account A debited, Account B not credited

Implementation: Write-Ahead Log (WAL) records changes before applying them. On crash, replay or undo based on WAL state.

Consistency: Constraints Preserved

Transactions transition the database from one valid state to another. All constraints (foreign keys, unique, check) are enforced.

Example: Referential Integrity

-- orders.user_id must reference existing user
INSERT INTO orders (user_id, amount) VALUES (999, 50.00);
-- Fails if user 999 doesn't exist

Note: Application-level consistency (business rules) is your responsibility. The database only enforces declared constraints.

Isolation: Concurrent Transactions Don’t Interfere

Even when transactions run concurrently, the result is as if they ran serially.

Example: Preventing Double-Booking

-- Transaction A                    -- Transaction B
BEGIN;                              BEGIN;
SELECT * FROM rooms                 SELECT * FROM rooms
  WHERE available = true;             WHERE available = true;
-- Both see Room 1 available        -- Both see Room 1 available

UPDATE rooms SET available = false  UPDATE rooms SET available = false
  WHERE id = 1;                       WHERE id = 1;
COMMIT;                             COMMIT; -- One must fail!

Isolation Levels (trade-off: correctness vs performance):

LevelDirty ReadNon-Repeatable ReadPhantom
Read UncommittedPossiblePossiblePossible
Read CommittedPreventedPossiblePossible
Repeatable ReadPreventedPreventedPossible
SerializablePreventedPreventedPrevented

Durability: Committed = Permanent

Once a transaction commits, its changes survive any subsequent failure.

Implementation:

  1. Write to WAL (Write-Ahead Log) before responding
  2. fsync() to force data to disk
  3. On crash recovery, replay committed transactions from WAL

Trade-off: fsync() is slow (~10ms). Options:

  • Synchronous: Wait for disk, guaranteed durability
  • Asynchronous: Risk losing recent commits on crash

ACID in Practice

PostgreSQL Transaction

BEGIN;

-- All operations in one atomic unit
INSERT INTO orders (user_id, total) VALUES (1, 99.99);
UPDATE inventory SET quantity = quantity - 1 WHERE product_id = 123;
INSERT INTO payments (order_id, amount) VALUES (currval('orders_id_seq'), 99.99);

COMMIT;  -- All succeed, or all fail

Application Code Pattern

def transfer_money(from_account, to_account, amount):
    with db.transaction():  # BEGIN
        from_balance = db.query(
            "SELECT balance FROM accounts WHERE id = ?", from_account
        )
        if from_balance < amount:
            raise InsufficientFunds()

        db.execute(
            "UPDATE accounts SET balance = balance - ? WHERE id = ?",
            amount, from_account
        )
        db.execute(
            "UPDATE accounts SET balance = balance + ? WHERE id = ?",
            amount, to_account
        )
    # COMMIT happens automatically at end of 'with' block
    # ROLLBACK if exception raised

When ACID Isn’t Enough

Distributed Transactions

ACID within a single database is well-solved. Across databases? Much harder.

ApproachTrade-off
Two-Phase Commit (2PC)Blocking, single point of failure
Saga PatternEventual consistency, compensation logic
Event SourcingAppend-only, rebuild state from events

Performance at Scale

Strict ACID limits throughput:

  • Locks reduce concurrency
  • fsync() adds latency
  • Cross-partition transactions coordinate

Solutions:

  • Lower isolation levels where safe
  • Partition data to avoid distributed transactions
  • Use eventual consistency where appropriate (BASE)

ACID vs BASE

For distributed systems that prioritize availability:

PropertyACIDBASE
ConsistencyStrong (immediate)Eventual
AvailabilityMay sacrificeHighly available
StateAlways validSoft state (may change)
Use CaseBanking, paymentsSocial media, analytics

Related Concepts:

Used In Systems:

  • PostgreSQL, MySQL, SQL Server (single-node ACID)
  • Google Spanner, CockroachDB (distributed ACID)
  • Kafka (transactional messaging)

Explained In Detail:

  • Database Internals Deep Dive - How WAL, isolation, and durability work under the hood

Next Recommended: Write-Ahead Log - Learn how databases implement durability

Production signal

Why this concept matters

Interview 80% of database-related interviews
Production Every transactional database
Performance Isolation level trade-offs
Scale Single-node vs distributed transactions