Transactions, ACID & Concurrency
A transaction is a sequence of database operations treated as a single unit: either everything succeeds, or nothing does. Transactions are how databases protect your data from crashes, bugs, and concurrent users stepping on each other.
What is a Transaction?#
If the server crashes between the two UPDATE statements, the partially-applied transfer would leave Alice's account debited but Bob's not credited — money destroyed. The transaction prevents this: either both updates apply, or neither does.
ACID Properties#
Atomicity#
The transaction is all-or-nothing. If any statement fails (or the server crashes), the entire transaction is rolled back as if it never happened.
Consistency#
A transaction takes the database from one valid state to another valid state. All constraints — NOT NULL, UNIQUE, foreign keys, CHECK constraints — must hold after every commit.
Isolation#
Concurrent transactions behave as if they run serially — they don't see each other's uncommitted changes.
Durability#
Once a transaction commits, the changes survive crashes. The DB writes a Write-Ahead Log (WAL) to disk before applying changes. On restart, uncommitted WAL entries are discarded; committed ones are replayed.
Concurrency Problems#
Without careful isolation, concurrent transactions cause anomalies:
Dirty Read#
Reading uncommitted changes from another transaction. If that transaction rolls back, you read data that never existed.
Non-Repeatable Read#
Reading the same row twice within a transaction and getting different values because another transaction committed a change in between.
Phantom Read#
Running the same query twice and getting different sets of rows because another transaction inserted or deleted rows in between.
Isolation Levels#
SQL defines four isolation levels, trading consistency for concurrency:
| Level | Dirty Read | Non-Repeatable Read | Phantom Read | How |
|---|---|---|---|---|
| Read Uncommitted | Possible | Possible | Possible | No locks |
| Read Committed | No | Possible | Possible | Lock on write, release on read |
| Repeatable Read | No | No | Possible (MySQL: No) | Hold read locks until commit |
| Serializable | No | No | No | Full serial-equivalent execution |
Most production databases default to Read Committed (PostgreSQL, SQL Server) or Repeatable Read (MySQL InnoDB).
MVCC — Multi-Version Concurrency Control#
Modern databases (PostgreSQL, MySQL InnoDB, Oracle) implement isolation using MVCC rather than locking everything:
- Each row maintains multiple versions with timestamps
- Writers create new versions; old versions remain for existing readers
- Readers see a snapshot of the database as of their transaction start time
- No read-write conflicts — readers never block writers
Savepoints#
You can create named checkpoints within a transaction and roll back to them partially:
Deadlocks in the Database#
Two transactions can deadlock if each holds a lock the other needs:
The database detects this and kills one transaction (the "victim"). The victim's transaction is rolled back automatically — the application should retry.
Prevention: always lock rows in the same order across all transactions.
Python: transactions with psycopg2#
Key Takeaways#
- Transactions are all-or-nothing: COMMIT applies everything, ROLLBACK undoes everything
- ACID guarantees data integrity across failures and concurrent users
- Isolation levels trade performance for correctness: higher isolation = fewer anomalies = more locking
- MVCC lets readers and writers proceed concurrently without blocking each other
- Database deadlocks are detected automatically; design transactions to avoid them by locking in consistent order