Transactions & Consistency· 10 min

ACID Properties

Atomicity, Consistency, Isolation, Durability

TransactionsBeginner10 min read

The four guarantees that relational databases make for transactions: Atomicity, Consistency, Isolation, Durability.

ACID Properties — diagram
BEGIN TRANSACTIONUPDATE accounts SET bal = bal - 100UPDATE accounts SET bal = bal + 100COMMITAAtomicityIf Op2 fails,Op1 rolled backCConsistencyConstraintsmust holdIIsolationConcurrent txnsdon't interfereDDurabilitySurvives crashvia WALRelational DBs guarantee all four · NoSQL relaxes to BASEPostgreSQL · MySQL → ACID · Cassandra · DynamoDB → BASE

Explanation

Atomicity means all operations in a transaction succeed or none do (all-or-nothing). Consistency means the database moves from one valid state to another. Isolation means concurrent transactions don't interfere. Durability means once committed, data survives crashes via a write-ahead log (WAL).

Example

BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
-- If either fails, ROLLBACK undoes both (Atomicity)
COMMIT; -- Durability: safe even if power dies

Key points

  • →Atomicity via undo logs
  • →Isolation levels trade off consistency for performance
  • →Durability via WAL fsync
  • →Consistency is enforced by constraints

Quick interview answers

Can NoSQL be ACID?
Some — MongoDB supports multi-document ACID since 4.0. Most trade ACID for availability/scalability (BASE).
ACID Consistency vs CAP Consistency?
ACID = obeys constraints. CAP = all nodes see same data at same time (linearizability).

DBMS deep dive

ACID Properties: deep dive

ACID is a promise about what happens when things go wrong: a crash, a constraint violation or two users touching the same row. Knowing which engine component keeps each promise is what separates a memorised answer from a strong interview answer.

Think in failures, not definitions

Each letter in ACID protects against a specific failure. Atomicity handles a transaction that dies halfway. Consistency handles a transaction that would break a rule such as a foreign key, a CHECK or a unique constraint. Isolation handles two transactions that run at the same time and see each other's half-finished work. Durability handles power loss right after the database said COMMIT succeeded. If you can name the failure, you can explain the mechanism.

Worked example: a crash in the middle of a transfer

Say ₹500 moves from account A to account B. The engine debits A, and the server loses power before it credits B. On restart, the database replays its log. It finds the debit's log records but no commit record for that transaction, so it treats the transaction as aborted and the debit never becomes visible. Now take the case where the commit record was flushed to disk just before the crash, but the changed data pages were still only in memory. Recovery replays the log (redo), and both the debit and the credit come back. The client was told the commit succeeded, so it has to survive.

The key point is that the log reaches disk before the data pages do. That rule is called write-ahead logging, and it is why a commit only has to wait for one fast sequential log write instead of many random page writes.

Which component keeps which promise

PropertyFailure it preventsPostgreSQLMySQL InnoDB
AtomicityPartial transaction left behindAborted transaction IDs are marked in pg_xact, so their row versions are never visibleUndo log rolls back the changes
ConsistencyConstraint or invariant brokenConstraints checked per statement, or at commit if DEFERRABLEConstraints checked immediately, FKs row by row (no deferred constraints)
IsolationInterference between concurrent transactionsMVCC snapshots plus SSI for SERIALIZABLEMVCC through undo plus next-key locks
DurabilityCommitted data lost on crashWAL fsync at commit (synchronous_commit)Redo log flush (innodb_flush_log_at_trx_commit=1), doublewrite buffer

Consistency is the odd one out. The database enforces only the rules you declare. If the invariant 'balance never goes negative' is not written as a CHECK constraint or guarded by a trigger, the database cannot keep it for you. That part of C belongs to the application.

Partial rollback with savepoints

Atomicity applies to the whole transaction, but savepoints let you undo part of one. This is useful when you try something optional inside a larger unit of work:

Savepoint inside a transfer
BEGIN;
UPDATE accounts SET balance = balance - 500 WHERE id = 'A';
UPDATE accounts SET balance = balance + 500 WHERE id = 'B';

SAVEPOINT before_reward;
INSERT INTO reward_points (account_id, points) VALUES ('A', 5);
-- Suppose this fails on a constraint: undo only the reward
ROLLBACK TO SAVEPOINT before_reward;

COMMIT;  -- the transfer still commits atomically

-- Make the invariant enforceable (the C in ACID)
ALTER TABLE accounts ADD CONSTRAINT balance_non_negative CHECK (balance >= 0);

In PostgreSQL, any error inside a transaction puts it in an aborted state, and every later statement fails until you ROLLBACK or roll back to a savepoint. Drivers that 'retry the statement' without a savepoint will keep failing. This trips people up in production.

Durability has knobs

Durability is configurable, and interviewers like to probe this. With PostgreSQL's synchronous_commit = off, or InnoDB's innodb_flush_log_at_trx_commit = 2, commits return before the log is fsynced. Throughput goes up, but a crash can lose the last fraction of a second of acknowledged commits. Group commit is the middle ground: many concurrent commits share one fsync, so you keep full durability at a much lower cost per commit.

Common mistakes

  • Assuming ACID holds across services. Two databases, or a database plus a message queue, need two-phase commit, a saga or the transactional outbox pattern.
  • Forgetting autocommit. Without an explicit BEGIN, every statement is its own transaction, so a multi-step transfer is not atomic.
  • Treating isolation as all-or-nothing. Most engines default to READ COMMITTED or REPEATABLE READ, not SERIALIZABLE. See transaction isolation levels.
  • Believing an fsync to disk is durable on hardware with volatile write caches and no power-loss protection.
  • Holding transactions open across network calls, which keeps locks held and blocks cleanup of old row versions.

Interview questions

›What is write-ahead logging, and why does it make commits fast?

Every change is described in a sequential log, and the log is flushed before the matching data pages are written. A commit waits only for the log flush, which is sequential and can be batched with other commits. Data pages are written back lazily, and redo replay rebuilds them after a crash.

›How does PostgreSQL provide atomicity without an undo log?

An UPDATE writes a new row version and leaves the old one in place. If the transaction aborts, its ID is marked aborted in pg_xact. Visibility checks then ignore its new versions, and the old versions stay current. Vacuum removes the dead versions later.

›What is the difference between redo and undo?

Redo reapplies committed changes that had not reached the data files, which gives durability. Undo reverses changes from transactions that did not commit, which gives atomicity. InnoDB also uses undo records to rebuild older row versions for MVCC readers.

›Is a single UPDATE statement atomic without BEGIN?

Yes. In autocommit mode each statement is its own transaction, so one UPDATE touching a million rows either applies to all of them or to none. Atomicity across several statements needs an explicit transaction.

›How would you keep a transfer atomic across two microservices?

You cannot get a local ACID transaction across services. The usual options are a saga, where each step has a compensating action, the transactional outbox, where the event is written in the same local transaction and published later, or two-phase commit, which blocks if the coordinator fails.

More Database Management Systems

Compare this lesson with the other 8 concepts in the course. Expand any card for its design, trade-offs and diagram.

Category:
Difficulty:
Showing 8 of 8