By WebNest Studio Engineering Team
Database and SQL Tutorial
Transaction Isolation Levels
Isolation is one of the four ACID properties, but it is not all-or-nothing — SQL defines four standard isolation levels that trade off consistency guarantees against performance. Stricter isolation prevents more anomalies but generally requires more locking or overhead, which can reduce concurrency (how many transactions can run at once without blocking each other).
Understanding isolation levels means understanding three specific anomalies they are designed to prevent: dirty reads, non-repeatable reads, and phantom reads.
Why it matters
When two users change the same data at the same moment, the result depends on how strictly the database keeps their transactions apart. Too little isolation gives wrong results, such as two people buying the last item; too much slows everything down. Choosing a level, and knowing your database's default, is part of building any system with concurrent users.
The Three Anomalies
Each anomaly describes a specific way that concurrent transactions can produce surprising, incorrect-looking results if isolation is too weak.
- Dirty read — a transaction reads data that another transaction has written but not yet committed. If that other transaction rolls back, the first transaction acted on data that never actually existed.
- Non-repeatable read — a transaction reads the same row twice and gets different values, because another transaction committed an UPDATE to that row in between the two reads.
- Phantom read — a transaction re-runs the same query twice and gets a different set of rows (extra or missing rows), because another transaction committed an INSERT or DELETE matching the query's condition in between.
The Four Standard Isolation Levels
From weakest to strongest, each level prevents more anomalies than the one before it, at the cost of more restrictive concurrency control.
- Read Uncommitted — the weakest level; allows dirty reads, non-repeatable reads, and phantom reads. Rarely used in practice; mainly useful for approximate reporting where perfect accuracy isn't required.
- Read Committed — prevents dirty reads (you only ever see committed data), but non-repeatable reads and phantom reads can still occur. This is the default isolation level in PostgreSQL and Oracle.
- Repeatable Read — prevents dirty reads and non-repeatable reads (a row you've already read will return the same values if read again), but phantom reads can still occur in the SQL standard's strict definition. This is the default isolation level in MySQL's InnoDB engine.
- Serializable — the strongest level; prevents dirty reads, non-repeatable reads, and phantom reads by making concurrent transactions behave as though they ran one after another in some serial order. It offers the strongest guarantees but has the highest risk of transactions being blocked or forced to retry.
Examples
Setting an isolation level explicitly for a transaction
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT COUNT(*) FROM seats WHERE flight_id = 42 AND is_booked = false;
-- Application decides there is a free seat and proceeds to book it
UPDATE seats SET is_booked = true
WHERE flight_id = 42 AND is_booked = false
LIMIT 1;
COMMIT;
count: 1
UPDATE 1
COMMIT
(Under SERIALIZABLE, a concurrent transaction attempting the same booking is forced to wait or retry, preventing two passengers from being assigned the same last seat.)
A non-repeatable read under Read Committed
-- Transaction 1
BEGIN;
SELECT price FROM products WHERE id = 10; -- returns 49.99
-- (Transaction 2, running concurrently, commits: UPDATE products SET price = 39.99 WHERE id = 10;)
SELECT price FROM products WHERE id = 10; -- returns 39.99, a different value in the same transaction
COMMIT;
price: 49.99 -- first read
price: 39.99 -- second read, changed because another transaction committed in between
Common Mistakes
- Assuming "Serializable" is always the right choice — it maximizes correctness but can significantly reduce throughput under heavy concurrent load due to increased blocking and retries.
- Confusing Repeatable Read with "the query results never change" — it guarantees a previously read row stays the same, but the exact phantom-read guarantee varies by database implementation (MySQL's InnoDB Repeatable Read actually prevents phantom reads too, going beyond the SQL standard's minimum).
- Not realizing the default isolation level differs between database engines (Read Committed in PostgreSQL/Oracle vs. Repeatable Read in MySQL/InnoDB), which can cause the same application code to behave differently after a database migration.
- Using Read Uncommitted for anything beyond rough approximate reporting, since dirty reads can lead to acting on data that is later rolled back and never actually existed.
Exercise
This exercise needs a real database server and two connections, so it cannot run in the playground. In PostgreSQL, write a transaction that runs at the REPEATABLE READ level and reads the balance of account 1 twice, so that both reads are guaranteed to return the same value even if another session commits a change in between.
Expected result: both SELECT statements return the same balance; at the default READ COMMITTED level the second could differ.
-- TODO: begin a transaction
-- TODO: set the isolation level to REPEATABLE READ
-- TODO: read the balance of account 1
-- (another session updates and commits the balance here)
-- TODO: read the balance again, then commit
Show hints
- The level is set with
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, as the first statement afterBEGIN. - It must come before any query in the transaction.
Show solution
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT balance FROM accounts WHERE id = 1;
-- (another session updates and commits the balance here)
SELECT balance FROM accounts WHERE id = 1;
COMMIT;
Quick quiz
1. What is a dirty read?
- A. Reading a row twice and getting different values
- B. Reading rows in the wrong order
- C. Reading data that another transaction has changed but not yet committed
- D. Reading a deleted table
Show answer
C. If that other transaction rolls back, the value that was read never really existed.
2. Which isolation level is the strictest?
- A. SERIALIZABLE
- B. READ COMMITTED
- C. READ UNCOMMITTED
- D. REPEATABLE READ
Show answer
A. The result is the same as if the transactions had run one after another.
3. What is the default isolation level in PostgreSQL?
- A. READ UNCOMMITTED
- B. REPEATABLE READ
- C. SERIALIZABLE
- D. READ COMMITTED
Show answer
D. MySQL's InnoDB engine defaults to REPEATABLE READ.
Interview questions
What are the read phenomena that isolation levels prevent?
Show answer
A dirty read is seeing another transaction's uncommitted change. A non-repeatable read is reading the same row twice in one transaction and getting different values, because another transaction committed a change in between. A phantom read is running the same query twice and getting a different set of rows, because another transaction inserted or deleted rows that match.
Describe the four standard isolation levels.
Show answer
READ UNCOMMITTED permits dirty reads. READ COMMITTED shows only committed data, but the same row may change between two reads. REPEATABLE READ guarantees that rows already read will not change within the transaction; the standard still allows phantoms at this level. SERIALIZABLE prevents all three phenomena and behaves as if transactions ran one at a time. Stricter levels mean more blocking or more transactions that must be retried.
Key Points to Remember
- Three anomalies define isolation strength: dirty reads, non-repeatable reads, and phantom reads.
- Four standard levels, weakest to strongest: Read Uncommitted, Read Committed, Repeatable Read, Serializable.
- Stronger isolation prevents more anomalies but generally costs more concurrency and performance.
- Default isolation level varies by database — always check rather than assume.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs your SQL on SQLite in your browser. Standard SQL works; a few features that exist only in PostgreSQL or MySQL do not. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
These examples need a project environment on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering