Skip to content
webkida wiki
Go back

Transaction isolation levels

Walks through the four transaction isolation levels in MySQL, plus dirty reads, non-repeatable reads, and phantom reads

How far concurrent transactions may see each other’s updates — that is the isolation level. The same SELECT can return different results depending on the level. MySQL’s default is Repeatable Read.

We’ll run this on a local MySQL and check the four levels, plus the read problems that can happen at each.

Terms

Isolation level

A setting that decides how far a running transaction may read updates from other transactions. You can switch it per session.

Dirty Read

Reading a change another transaction has not committed yet. If that transaction later rolls back, what you read no longer matches the real data.

Non-repeatable Read

Reading the same row again inside the same transaction, and the value changing because another transaction committed an update.

Phantom Read

Re-running a query inside the same transaction, and the set of rows changing because another transaction inserted or deleted rows. The focus is rows appearing or disappearing, not updates.

MVCC

A mechanism that reads a snapshot. Inserts and deletes are also seen from the snapshot side, so a phantom read may not appear.

Isolation levels

We’ll confirm each level’s behavior by actually running it on MySQL. Commands you will use often:

-- 分離レベルの確認
select @@transaction_isolation;
-- 分離レベルの設定(セッション単位)
set session transaction isolation level <分離レベル>;
-- トランザクションの開始
start transaction;
begin;
-- コミット
commit;
-- ロールバック
rollback;

For setting details, see MySQL SET TRANSACTION.

Read Uncommitted

The lowest isolation level. One transaction can read data another transaction has not committed yet.

Overview of Read Uncommitted

Figure: Right after Tran B updates age=40, Tran A can already see 40, even before the commit.

Set Tran A’s session to Read Uncommitted.

-- 分離レベルの設定: Read Uncommitted
set session transaction isolation level read uncommitted;

Setting Read Uncommitted

Start a transaction on Tran A and read user1’s age: it is 30.

Tran A's first read (age=30)

Next, start a transaction on Tran B and update age to 40. Do not commit yet.

Tran B updates age=40 (uncommitted)

If Tran A reads again in this state, Tran B’s uncommitted update is visible (age=40).

Tran A reads the uncommitted age=40

Read Committed

A widely adopted isolation level. A transaction can read only data that other transactions have committed.

Overview of Read Committed

Figure: Tran B’s update is visible to Tran A only after the commit. The same SELECT changes age from 30 to 40 around the commit.

The main databases whose default this is are Oracle, SQL Server, and PostgreSQL.

Set Tran A’s session to Read Committed.

-- 分離レベルの設定: Read Committed
set session transaction isolation level read committed;

Setting Read Committed

Start a transaction on Tran A and read user1’s age: it is 30.

Tran A's first read (age=30)

Next, start a transaction on Tran B and update age to 40. Still no commit.

Tran B updates age=40 (uncommitted)

If Tran A reads again in this state, the value is still what it was before Tran B’s update (age=30).

Tran A does not see the uncommitted update (age=30)

Commit Tran B.

Tran B commits

If Tran A reads again in this state, Tran B’s now-committed update is visible (age=40).

Tran A reads age=40 after the commit

Repeatable Read

A higher-consistency isolation level. A snapshot is taken at the start of the transaction, and until it ends, updates from other transactions are not visible.

Overview of Repeatable Read

Figure: Even after Tran B commits age=40, Tran A still sees age=30 for the rest of its transaction. After Tran A commits, a read shows 40.

MySQL’s default is this Repeatable Read.

Set Tran A’s session to Repeatable Read.

-- 分離レベルの設定: Repeatable Read
set session transaction isolation level repeatable read;

Setting Repeatable Read

Start a transaction on Tran A and read user1’s age: it is 30.

Tran A's first read (age=30)

Next, start a transaction on Tran B, update age to 40, and commit.

Tran B updates age=40 and commits

If Tran A reads again in this state, Tran B’s update is still not visible. You can repeatedly read the pre-update age=30.

Tran A does not see even the committed update (age=30)

After Tran A commits, a read shows the post-update age=40.

After Tran A commits, age=40

Serializable

The isolation level that tries to guarantee the highest consistency. While one transaction is updating data, another transaction that tries to read that data may wait. Consistency is the highest, while concurrency drops and performance can be affected.

Overview of Serializable

Figure: Tran A’s SELECT does not return a result until Tran B commits.

Set Tran A’s session to Serializable.

-- 分離レベルの設定: Serializable
set session transaction isolation level serializable;

Setting Serializable

Start a transaction on Tran A.

Tran A starts a transaction

Start a transaction on Tran B as well.

Tran B starts a transaction

Insert data on Tran B.

Tran B inserts a row

Read the data on Tran A. It goes into a wait and the result does not come back right away.

Tran A's SELECT waits

Commit Tran B.

Tran B commits

The result Tran A was waiting for comes back.

The wait ends and Tran A gets a result

Isolation problems

Dirty reads, non-repeatable reads, and phantom reads are read problems that can occur at each isolation level. The lower the isolation level, the more likely they are.

ProblemLevels where it can occurWhat happens
Dirty readRead UncommittedUncommitted updates are visible
Non-repeatable readRead Committed and belowRe-reading the same row changes the value
Phantom readRepeatable Read and belowInserts or deletes change the set of rows

Dirty Read

If a Read Uncommitted transaction (Tran A) reads data, and another transaction (Tran B) then updates that data and rolls back, you get a mismatch. What Tran A read does not match the actual state.

In the next example, Tran A reads age=40. Tran B’s update is undone by rollback, so the real data is age=30.

Overview of a dirty read

Figure: The age=40 Tran A read disappears when Tran B rolls back. The value that was read does not match the real data.

Non-repeatable Read

At Read Committed and below, reading the same data more than once inside the same transaction can produce a different result the second time. It happens when another transaction changes the data and commits. To prevent it, use Repeatable Read or Serializable.

That said, a non-repeatable read is not necessarily a problem. You can also see it as the isolation level behaving correctly. Many databases default to Read Committed, so this behavior can happen. In practice it is often acceptable; what matters is choosing a level that fits the database design and the application requirements.

Phantom Read

At Repeatable Read and below, if another transaction inserts or deletes rows while yours is running, a later query may pick up that change. The difference from a non-repeatable read is that the focus is inserts and deletes, not updates to existing rows.

Overview of a phantom read

Figure: A phantom read as defined. A row is inserted in the middle, and the visible count changes.

Many articles write that “phantom reads occur at Repeatable Read.” It did not occur on my machine. With a snapshot-reading mechanism such as MVCC, inserts and deletes are also seen from the snapshot side. So phantom reads apparently do not appear.

Reference: Phantom reads and MySQL Repeatable Read

I tried to reproduce it on MySQL, and a phantom read did not occur.

-- ① トランAでトランザクションを開始
start transaction;
-- ② トランAでデータ参照(データ1件のみある)
select * from employee;
-- ③ トランBでトランザクションを開始
start transaction;
-- ④ トランBでデータ追加
insert into employee values(2,'user2',20);
-- ⑤ トランBでコミット
commit;
-- ⑥ トランAで再びデータ参照
select * from employee;

The expected value for a phantom read is that step ⑥ sees the row Tran B added in step ④. As a result, it was not visible. To see a reproduction, you would likely need Read Committed or below.

Takeaways

The isolation level decides how far concurrent transactions see each other’s updates. MySQL’s default is Repeatable Read.

Read Uncommitted can see even uncommitted updates, so it is almost never used in practice. At Read Committed, non-repeatable reads can occur, but that is also the behavior many databases adopt. Phantom reads are said to be possible at Repeatable Read and below. On MySQL, though, reads go to an MVCC snapshot, and I could not reproduce one locally.

The index-side write-up is in SQL Server indexes and execution plans.


Share this post:

Previous Post
【System Design】 CQRS (Command Query Responsibility Segregation)
Next Post
【Job Change】Going Freelance as a Java Engineer in My Late 20s!