SQL Transaction Isolation Levels

READ UNCOMMITTED through SERIALIZABLE with anomaly prevention and lock behavior.

Reference for the four ANSI SQL transaction isolation levels with the read anomalies each prevents (dirty, non-repeatable, phantom) and the default level per major database. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

What are the four ANSI SQL isolation levels?

From weakest to strongest: Read Uncommitted, Read Committed, Repeatable Read and Serializable. Each higher level prevents more concurrency anomalies at the cost of more locking or aborts, so stronger isolation trades throughput for correctness.

Choosing an isolation level

A transaction’s isolation level controls how much it is shielded from concurrent transactions. The ANSI SQL standard defines four levels — Read Uncommitted, Read Committed, Repeatable Read and Serializable — each ruling out more read anomalies as you go stronger, in exchange for more locking, blocking or aborts. This reference shows which anomalies each level allows and the default level chosen by the major databases.

Anomaly grid

Level             Dirty read   Non-repeatable   Phantom
Read Uncommitted  possible     possible         possible
Read Committed    prevented    possible         possible
Repeatable Read   prevented    prevented        possible*
Serializable      prevented    prevented        prevented

* Several engines (PostgreSQL snapshot, MySQL InnoDB next-key locks) also block phantoms at Repeatable Read, exceeding the bare ANSI requirement.

What each anomaly means in practice

Dirty read — Transaction A reads a row that Transaction B has modified but not yet committed. If B rolls back, A has seen data that never officially existed. Classic scenario: a reservation system where A reads a seat as “booked” but B is mid-cancellation and rolls back.

Non-repeatable read — Transaction A reads a row, then reads it again and sees a different value because B committed an update in between. Classic scenario: an audit report that reads the same account balance twice during its run and gets different numbers.

Phantom read — Transaction A queries a set of rows, then re-queries and sees additional rows because B inserted matching rows and committed. Classic scenario: a report counting active users that grows mid-run because new signups are committed by concurrent transactions.

Setting the level

-- Per-transaction (most portable)
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
-- ... your queries ...
COMMIT;

-- Per-session (PostgreSQL)
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- Per-session (MySQL)
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

Default levels by database

DatabaseDefault levelImplementation notes
PostgreSQLRead CommittedRepeatable Read uses MVCC snapshots; Serializable uses SSI
MySQL InnoDBRepeatable ReadNext-key locking prevents most phantoms at this level
SQL ServerRead CommittedRCSI (snapshot) available; Read Uncommitted usable for reporting
OracleRead CommittedOnly supports Read Committed and Serializable
SQLiteSerializableSingle-writer model; isolation is implicit

Practical guidance

  • Read Committed is the correct default for most OLTP workloads. It avoids dirty reads while keeping concurrency high.
  • Repeatable Read is worth enabling for long-running transactions that re-read the same rows, such as complex reporting jobs.
  • Serializable is appropriate for financial transactions with correctness invariants that span multiple rows (e.g., double-entry accounting). Design for retry loops at this level, because the engine may abort conflicting transactions.
  • Read Uncommitted is almost never useful in production. It lets you read dirty data, which usually causes more bugs than the marginal throughput gain is worth. PostgreSQL silently upgrades it to Read Committed anyway.