ACID
ACID
Learn how Atomicity, Consistency, Isolation, and Durability make database transactions reliable during application failures, concurrent updates, constraint violations, and system crashes.
Introduction
Database operations frequently involve several related changes. Creating an order can require inserting the order header, adding order lines, reserving inventory, recording payment state, and updating totals.
If only some of these changes succeed, the database can contain incomplete or contradictory information.
ACID describes four important properties of reliable database transactions:
- Atomicity: The transaction completes as one indivisible unit or has no effect.
- Consistency: A committed transaction preserves the defined integrity rules.
- Isolation: Concurrent transactions are controlled so that unsafe interference is prevented according to the selected isolation level.
- Durability: Once a successful commit is acknowledged, the committed changes survive the failures covered by the database's durability guarantee.
Core idea: ACID allows several database statements to be treated as one reliable business operation. The database either commits the valid operation or prevents a partial result from becoming the final stored state.
In your System Design curriculum, ACID is Topic 5.4 under Relational Data Modeling & SQL. It follows keys and constraints and precedes transactions, isolation, B-tree and composite indexes, query plans, and connection pools.
Prerequisites
| # | Prerequisite | Why It Is Needed |
|---|---|---|
| 1 | Entities and relationships | Transactions frequently modify several related entities. |
| 2 | Normalization | One business operation commonly updates several normalized tables. |
| 3 | Keys and constraints | Consistency depends on defined primary, foreign, unique, and check constraints. |
| 4 | Basic SQL | The examples use INSERT, UPDATE, SELECT, COMMIT, and ROLLBACK. |
| 5 | Concurrency basics | Isolation controls interference between concurrent transactions. |
| 6 | Failure handling | Atomicity and durability define behaviour when operations or systems fail. |
What Is a Database Transaction?
A database transaction is one logical unit of work containing one or more database operations.
Begin transaction
|
v
Read required data
|
v
Perform related changes
|
v
Validate constraints and business rules
|
+--> Success:
| COMMIT
|
+--> Failure:
ROLLBACK
The transaction boundary should normally represent a complete business operation rather than an arbitrary number of SQL statements.
Order Example
Business operation:
Create order
Database work:
1. Insert order header.
2. Insert order lines.
3. Reserve inventory.
4. Store order total.
5. Record idempotency outcome.
Required result:
All related changes succeed,
or none becomes the committed result.
The Four ACID Properties
| Property | Primary Question | Guarantee |
|---|---|---|
| Atomicity | What happens when part of the operation fails? | The transaction commits completely or is rolled back |
| Consistency | Does the transaction preserve valid database states? | Defined constraints and invariants remain satisfied |
| Isolation | How do concurrent transactions interact? | Unsafe interference is controlled according to the isolation model |
| Durability | What happens after a successful commit? | Committed changes survive covered failures |
Atomicity
Atomicity means that a transaction is treated as one indivisible unit. All its changes are committed, or none of its changes remains as the committed result.
Transfer Example
Transfer 100 units from Account A to Account B
Step 1:
Subtract 100 from Account A.
Step 2:
Add 100 to Account B.
If the system applies the first change but not the second, value disappears from the modeled system. Atomicity prevents this partial result from becoming the committed database state.
any required operation fails → roll back transaction
SQL Transfer Transaction
BEGIN;
UPDATE accounts
SET balance = balance - 100.00
WHERE account_id = 101
AND balance >= 100.00;
UPDATE accounts
SET balance = balance + 100.00
WHERE account_id = 202;
COMMIT;
Production logic must verify that the expected rows were updated. If the source account does not exist, has insufficient funds, or another required condition fails, the application should roll back the transaction.
Rollback Example
BEGIN;
UPDATE accounts
SET balance = balance - 100.00
WHERE account_id = 101
AND balance >= 100.00;
-- Required validation or second update fails.
ROLLBACK;
After rollback, the transaction's uncommitted modifications are discarded.
Atomicity Is Not the Same as Idempotency
| Concept | Concern |
|---|---|
| Atomicity | Prevents a single transaction from leaving a partial committed result |
| Idempotency | Prevents repeated execution of one logical operation from creating repeated intended effects |
An order-creation transaction can be atomic and still create two orders when the complete transaction is executed twice. Retryable operations still need an idempotency strategy.
Retry rule: Atomicity protects one execution. Idempotency protects the business effect across repeated executions.
Consistency
Consistency means that a successful transaction moves the database from one valid state to another valid state while preserving the defined integrity rules.
Consistency rules can include:
- Primary-key uniqueness
- Foreign-key validity
- Required values
- Allowed status values
- Valid numeric ranges
- Correct totals
- Inventory rules
- Account-balance rules
- Valid lifecycle transitions
- Tenant-boundary rules
Constraint-based Consistency
CREATE TABLE accounts
(
account_id BIGINT PRIMARY KEY,
balance DECIMAL(18, 2) NOT NULL,
CONSTRAINT ck_accounts_balance
CHECK (balance >= 0)
);
The CHECK constraint prevents a committed row from containing a negative balance when that rule accurately represents the domain.
Referential Consistency
CREATE TABLE orders
(
order_id BIGINT PRIMARY KEY,
customer_id BIGINT NOT NULL,
CONSTRAINT fk_orders_customer
FOREIGN KEY (customer_id)
REFERENCES customers (customer_id)
);
The foreign key prevents a committed order from referencing a nonexistent customer.
Business Invariant
An order total can be represented by the invariant:
\[ OrderTotal = \sum LineAmount + Tax + Shipping - Discounts \]
The application and database design must ensure that all committed changes preserve the required interpretation of this rule.
Responsibility rule: The database can enforce declared constraints, but developers must define correct constraints and write transactions that preserve business invariants.
ACID Consistency vs Distributed Consistency
ACID consistency and distributed-system consistency are related terms with different emphasis.
| Term | Primary Meaning |
|---|---|
| ACID consistency | A committed transaction preserves declared integrity rules and valid states |
| Replica consistency | How copies of data converge and what values readers can observe |
| Consistency model | The guarantees provided to concurrent or distributed readers and writers |
Saying that a database is ACID does not by itself fully describe replica visibility, cross-region behaviour, or every distributed guarantee.
Isolation
Isolation controls how concurrent transactions affect one another and which intermediate or committed changes each transaction can observe.
Transaction A
|
| reads and writes shared data
v
Database concurrency control
^
| reads and writes shared data
|
Transaction B
Without suitable isolation, concurrent transactions can produce outcomes that neither transaction intended.
Last-seat Example
Available seats:
1
Transaction A reads:
1 seat available.
Transaction B reads:
1 seat available.
Transaction A books the seat.
Transaction B also books the seat.
Incorrect result:
Two bookings for one seat.
A correct design uses appropriate locking, atomic conditional updates, uniqueness constraints, or stronger isolation to prevent both bookings from succeeding.
Atomic Conditional Update
UPDATE events
SET available_seats = available_seats - 1
WHERE event_id = :event_id
AND available_seats > 0;
The application must verify that exactly one row was updated before creating the confirmed booking.
Common Concurrency Anomalies
| Anomaly | Description |
|---|---|
| Dirty read | A transaction reads data written by another transaction that has not committed |
| Non-repeatable read | Reading the same row twice produces different committed values |
| Phantom read | Repeating a predicate query returns a different set of matching rows |
| Lost update | One transaction overwrites another transaction's change |
| Write skew | Concurrent transactions update different rows after reading a shared condition and violate a cross-row invariant |
Dirty-read Example
Transaction A:
Updates balance from 500 to 100.
Has not committed.
Transaction B:
Reads balance as 100.
Transaction A:
Rolls back.
Problem:
Transaction B acted on a value
that never became committed.
Non-repeatable-read Example
Transaction A:
Reads order status as pending.
Transaction B:
Updates status to confirmed.
Commits.
Transaction A:
Reads the same order again.
Now sees confirmed.
Phantom-read Example
Transaction A:
Counts 10 pending orders.
Transaction B:
Inserts another pending order.
Commits.
Transaction A:
Repeats the same query.
Now counts 11 pending orders.
Isolation Levels
Relational database systems commonly provide several isolation levels. Stronger isolation can prevent more anomalies but can increase blocking, retries, or concurrency-control work.
| Isolation Level | General Purpose |
|---|---|
| Read Uncommitted | Provides weak isolation and can permit observation of uncommitted changes |
| Read Committed | Prevents dirty reads while allowing some values or result sets to change between statements |
| Repeatable Read | Provides stronger repeatability for rows read by the transaction |
| Serializable | Aims to provide outcomes equivalent to a valid serial execution of committed transactions |
Exact isolation behaviour differs among database engines. Verify the selected platform's documentation and test the actual transaction pattern.
Durability
Durability means that after the database acknowledges a successful commit, the committed transaction survives failures covered by the database's durability configuration and storage architecture.
Transaction changes data
|
v
Database records durable recovery information
|
v
Database confirms COMMIT
|
v
System or process fails
|
v
Database recovers committed transaction
Durability is commonly supported through mechanisms such as:
- Transaction logs
- Write-ahead logging
- Durable storage
- Checkpoints
- Recovery processing
- Storage redundancy
- Replication where configured
Write-ahead Logging
With write-ahead logging, recovery information describing a change is written to the transaction log before the modified data page is considered safely persisted.
Change requested
|
v
Log record created
|
v
Log becomes durable
|
v
Commit acknowledged
|
v
Data pages can be written later
After a crash, the database can use its log and recovery rules to reapply committed changes and undo incomplete work.
Durability Is Not Backup
| Capability | Primary Purpose |
|---|---|
| Transaction durability | Preserves acknowledged committed changes across covered failures |
| Replication | Maintains additional copies for availability or read distribution |
| Backup | Provides a recoverable historical copy |
| Point-in-time recovery | Restores the database to an approved earlier time |
| Disaster recovery | Restores service after major infrastructure or regional failure |
Recovery rule: ACID durability does not replace backups, restore testing, replication planning, or disaster-recovery procedures.
ACID Order-creation Example
BEGIN;
INSERT INTO orders
(
order_id,
customer_id,
order_status,
total_amount
)
VALUES
(
:order_id,
:customer_id,
'pending',
:total_amount
);
INSERT INTO order_lines
(
order_id,
line_number,
product_id,
quantity,
unit_price
)
VALUES
(
:order_id,
1,
:product_id,
:quantity,
:unit_price
);
UPDATE inventory
SET available_quantity =
available_quantity - :quantity
WHERE product_id = :product_id
AND available_quantity >= :quantity;
-- Verify one inventory row was updated.
COMMIT;
Atomicity
The order, line, and inventory reservation commit together or are rolled back together.
Consistency
Foreign keys, quantity checks, totals, and inventory rules remain valid.
Isolation
Concurrent orders cannot both consume inventory that is available to only one order.
Durability
After commit is acknowledged, the created order remains stored according to the database's durability guarantee.
ACID Payment Example
Payment transaction:
1. Verify payment does not already exist.
2. Insert payment record.
3. Update order payment status.
4. Insert accounting entry.
5. Store idempotency outcome.
6. Commit.
The transaction protects database changes within its transaction boundary. A remote payment provider cannot generally participate in the same ordinary local database transaction. External side effects require additional workflow, reconciliation, and idempotency design.
Local Transactions vs Distributed Workflows
Local database transaction:
Orders table
Payments table
Accounting entries
Idempotency records
All stored in one transactional database.
Distributed workflow:
Order database
Payment provider
Inventory service
Messaging system
Several independent systems.
ACID guarantees from one database do not automatically create one atomic transaction across independent services.
Distributed workflows can require:
- Idempotency keys
- Transactional outbox patterns
- Durable workflow state
- Compensating actions
- Message deduplication
- Retries and reconciliation
Database Commit and Message Publication
1. Commit order to database.
2. Publish OrderCreated message.
3. Application fails before publication.
Result:
Order exists,
but no event was published.
Within one database transaction:
1. Insert order.
2. Insert outbox message.
3. Commit both.
After commit:
A publisher reads the outbox
and delivers the message reliably.
Outbox Table
CREATE TABLE outbox_messages
(
message_id VARCHAR(100) PRIMARY KEY,
event_type VARCHAR(100) NOT NULL,
aggregate_id VARCHAR(100) NOT NULL,
payload TEXT NOT NULL,
created_at TIMESTAMP NOT NULL,
published_at TIMESTAMP NULL
);
Consumers should still process duplicate deliveries idempotently because a publisher can deliver a message and fail before recording successful publication.
PHP Transaction Example
<?php
declare(strict_types=1);
function transferFunds(
PDO $pdo,
int $sourceAccountId,
int $targetAccountId,
string $amount
): void {
if (bccomp($amount, '0.00', 2) <= 0) {
throw new InvalidArgumentException(
'The transfer amount must be positive.'
);
}
$pdo->beginTransaction();
try {
$debit =
$pdo->prepare(
'
UPDATE accounts
SET balance = balance - :amount
WHERE account_id = :account_id
AND balance >= :amount
'
);
$debit->execute([
'amount' => $amount,
'account_id' => $sourceAccountId
]);
if ($debit->rowCount() !== 1) {
throw new RuntimeException(
'The source account is unavailable or has insufficient funds.'
);
}
$credit =
$pdo->prepare(
'
UPDATE accounts
SET balance = balance + :amount
WHERE account_id = :account_id
'
);
$credit->execute([
'amount' => $amount,
'account_id' => $targetAccountId
]);
if ($credit->rowCount() !== 1) {
throw new RuntimeException(
'The target account does not exist.'
);
}
$pdo->commit();
} catch (Throwable $exception) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
throw $exception;
}
}
This example uses a decimal string rather than a floating-point value for the amount. Production code also needs authorization, idempotency, appropriate isolation, audit records, currency validation, and safe error handling.
Keep Transactions Short
Long-running transactions can retain locks, row versions, connections, log space, and other resources.
Begin transaction
Read order
Call external payment provider
Wait for network response
Generate document
Send email
Update database
Commit
Perform external preparation where safe
Begin transaction
Read required rows
Validate current state
Apply database changes
Insert outbox message
Commit
Process external follow-up asynchronously
Moving work outside a transaction is safe only when the workflow and revalidation rules account for changes that can occur before commit.
Deadlocks
A deadlock can occur when transactions wait for resources held by each other.
Transaction A:
Locks Account 1
Waits for Account 2
Transaction B:
Locks Account 2
Waits for Account 1
Result:
Circular wait
The database can detect the deadlock and abort one transaction. The application should treat the aborted transaction according to its documented retry and idempotency policy.
Deadlock-reduction practices include:
- Access shared resources in a consistent order
- Keep transactions short
- Use suitable indexes
- Avoid unnecessary rows and tables in the transaction
- Implement bounded retries for safe operations
Retrying Transactions
Transaction fails because of
a transient concurrency conflict
|
v
Roll back completely
|
v
Is the complete business operation
safe to retry?
|
+--> No:
| return controlled failure
|
+--> Yes:
wait with backoff and jitter
start a new transaction
reread current state
retry within overall deadline
Do not continue using a transaction after it has failed or been rolled back. Begin a new transaction and reread the data required for the decision.
Savepoints
A savepoint establishes a named point inside a transaction to which selected later work can be rolled back where the database supports that behaviour.
BEGIN;
INSERT INTO orders
(
order_id,
customer_id
)
VALUES
(
:order_id,
:customer_id
);
SAVEPOINT before_optional_note;
INSERT INTO order_notes
(
order_id,
note_text
)
VALUES
(
:order_id,
:note_text
);
ROLLBACK TO SAVEPOINT before_optional_note;
COMMIT;
Savepoints do not make independent commits. The outer transaction still commits or rolls back as one transaction.
ACID Observability
Useful transaction metrics include:
- Transaction count
- Commit count
- Rollback count
- Transaction duration
- Deadlock count
- Lock-wait duration
- Serialization or concurrency-conflict failures
- Constraint violations
- Transaction-log growth
- Recovery duration
- Connection time spent inside transactions
- Long-running transaction count
Logs should include safe operation and trace identifiers without exposing credentials, sensitive values, or raw internal database details.
ACID Testing Strategy
ACID behaviour should be tested through failures and concurrency rather than only through successful single-user operations.
- Identify one complete business transaction.
- Force failure after each database operation.
- Verify that no partial committed state remains.
- Attempt constraint-violating values.
- Run conflicting transactions concurrently.
- Test the selected isolation level.
- Force transaction rollback.
- Test deadlock or serialization-failure handling.
- Verify safe retry behaviour.
- Test recovery after process or database interruption in an approved environment.
- Verify committed and uncommitted outcomes.
- Confirm that external side effects are reconciled correctly.
Common ACID Mistakes
Using One Transaction per SQL Statement
Related statements can commit independently and leave a partial business operation.
Keeping Transactions Open during External Calls
Network calls inside transactions increase duration, locking, timeout, and deadlock risks.
Assuming Atomicity Provides Idempotency
An atomic transaction can still produce duplicate effects when the entire operation is executed more than once.
Ignoring Affected-row Counts
A conditional update can affect no rows. Committing without checking can record a false success.
Assuming the Database Defines Business Consistency
The database enforces declared rules. Missing or incorrect constraints allow invalid states to commit.
Using Weak Isolation without Analysis
Concurrent transactions can violate business invariants even when every individual statement succeeds.
Assuming Serializable Means No Retries
Strong isolation can abort conflicting transactions. Applications still need safe retry handling.
Using an Aborted Transaction Again
After rollback or certain database errors, start a new transaction and reread current state.
Assuming Commit Means an External Client Saw the Response
The transaction can commit while the response is lost. Clients need idempotency and outcome-recovery contracts.
Assuming Local ACID Covers Several Services
One database transaction does not automatically include external services, brokers, or payment providers.
Confusing Durability with Backup
Durable commits do not protect against every operator error, corruption, deletion, or disaster scenario.
Testing Only Successful Transactions
Atomicity, isolation, and recovery properties become visible under failure, concurrency, rollback, and restart conditions.
Recommended Test Cases
| Test | Expected Evidence |
|---|---|
| Successful transaction | All related changes commit |
| Failure after first update | No partial committed state remains |
| Constraint violation | The invalid transaction does not commit |
| Insufficient account balance | Neither debit nor credit is committed |
| Concurrent inventory purchase | Inventory is not oversold |
| Concurrent seat reservation | Only one transaction obtains the final seat |
| Deadlock victim | The aborted transaction rolls back completely |
| Safe transaction retry | The business operation follows its idempotency contract |
| Lost response after commit | The client can retrieve the committed outcome safely |
| Process failure before commit | Uncommitted changes are not retained as committed data |
| Approved recovery test | Committed changes survive the tested covered failure |
| Outbox publication failure | The committed message remains available for later publication |
ACID Best Practices
Recommended Practices
- Define transactions around complete business operations.
- Commit all required database changes together.
- Roll back after any unrecoverable transaction failure.
- Verify affected-row counts for conditional changes.
- Declare keys and constraints that represent valid states.
- Validate business invariants before commit.
- Select isolation levels from actual concurrency requirements.
- Use atomic conditional updates for contested resources.
- Keep transactions focused and short.
- Access shared resources in a consistent order.
- Design safe bounded retries for concurrency failures.
- Combine atomic transactions with idempotency for retryable operations.
- Do not hold local transactions open during slow external calls.
- Use durable workflow patterns across service boundaries.
- Publish database-derived events through a reliable outbox where appropriate.
- Make message consumers idempotent.
- Understand the database's durability configuration.
- Maintain tested backups and recovery procedures.
- Monitor rollbacks, deadlocks, lock waits, and long transactions.
- Test failures and concurrency using representative workloads.
Practice Exercise
Implement an ACID course-enrollment transaction for the online learning platform designed in the previous lessons.
Requirements
- Verify that the learner exists and is active.
- Verify that the course exists and accepts enrollment.
- Prevent duplicate enrollment.
- Check that capacity remains available.
- Create the enrollment.
- Reduce available course capacity.
- Create an enrollment audit record.
- Store an outbox event in the same transaction.
- Commit all changes together.
- Roll back when any required step fails.
- Prevent two learners from receiving one remaining seat.
- Handle deadlock or serialization failures safely.
- Use an idempotency key for retryable enrollment requests.
- Verify successful recovery after a lost response.
- Measure transaction duration and rollback count.
Suggested Transaction
BEGIN;
UPDATE courses
SET available_seats = available_seats - 1
WHERE course_id = :course_id
AND course_status = 'published'
AND available_seats > 0;
-- Verify exactly one course row was updated.
INSERT INTO enrollments
(
enrollment_id,
learner_id,
course_id,
enrollment_status,
completion_percentage,
enrolled_at
)
VALUES
(
:enrollment_id,
:learner_id,
:course_id,
'active',
0,
CURRENT_TIMESTAMP
);
INSERT INTO enrollment_audit
(
audit_id,
enrollment_id,
action_name,
occurred_at
)
VALUES
(
:audit_id,
:enrollment_id,
'enrollment_created',
CURRENT_TIMESTAMP
);
INSERT INTO outbox_messages
(
message_id,
event_type,
aggregate_id,
payload,
created_at
)
VALUES
(
:message_id,
'EnrollmentCreated',
:enrollment_id,
:payload,
CURRENT_TIMESTAMP
);
COMMIT;
ACID Review Template
| Property | Enrollment Guarantee | Verification |
|---|---|---|
| Atomicity | Seat reduction, enrollment, audit, and outbox record commit together | Force failure after each statement |
| Consistency | Capacity, uniqueness, status, and foreign-key rules remain valid | Attempt invalid and duplicate enrollments |
| Isolation | One remaining seat cannot create two successful enrollments | Run concurrent enrollment transactions |
| Durability | A committed enrollment survives tested covered failures | Perform an approved recovery test |
Frequently Asked Questions
What does ACID stand for?
ACID stands for Atomicity, Consistency, Isolation, and Durability.
What is atomicity?
Atomicity means that all required changes in a transaction commit together or none remains as the committed result.
What is consistency?
Consistency means that a committed transaction preserves the database's defined constraints and business invariants.
What is isolation?
Isolation controls interactions between concurrent transactions and the changes each transaction can observe.
What is durability?
Durability means that acknowledged committed changes survive failures covered by the database's durability configuration.
What is the difference between COMMIT and ROLLBACK?
COMMIT makes the successful transaction's changes final, while ROLLBACK discards its uncommitted changes.
Does atomicity prevent duplicate requests?
No. Atomicity protects one transaction execution. Idempotency is required to prevent repeated executions from creating duplicate effects.
Does ACID guarantee that business rules are correct?
No. ACID preserves the rules implemented by the schema and transaction logic. Developers must define the correct business constraints.
Does isolation mean transactions literally run one at a time?
No. Database systems can execute transactions concurrently while using concurrency-control mechanisms to provide the selected isolation guarantees.
Does durability replace backups?
No. Durability, backups, replication, point-in-time recovery, and disaster recovery protect against different failure scenarios.
Can one local transaction include several independent services?
Not through an ordinary local database transaction. Distributed workflows need additional coordination, idempotency, messaging, and reconciliation patterns.
What comes after ACID?
The next topic is transactions, followed by isolation and B-tree and composite indexes.
Key Takeaway
ACID makes database transactions reliable. Atomicity prevents partial committed operations, consistency preserves declared integrity rules, isolation controls unsafe interference between concurrent transactions, and durability preserves acknowledged commits across covered failures. Design transaction boundaries around complete business operations, keep transactions short, verify conditional updates, choose isolation from actual concurrency requirements, combine atomicity with idempotency, and use durable workflow patterns when work crosses database or service boundaries.