Skip to the content.

⭐ What Isolation Really Means

Isolation ensures:

Even if multiple transactions run at the same time, the final result is the same as if they ran one-by-one (serially).

Transactions should not see each other’s intermediate states, uncommitted changes, or dirty data.


⭐ Why do we even need Isolation?

Imagine 2 users transferring money from the same account at the same time:

If done without isolation → both may read the same balance → double-withdrawal → account becomes inconsistent.

Isolation prevents this.


⭐ How Isolation is implemented internally?

Databases use two main techniques:


1️⃣ Locks

The old-school method.

When a transaction reads or writes a row/page/table:

This prevents other transactions from touching the same data until it’s safe.

Problems:


2️⃣ MVCC (Multi-Version Concurrency Control)

Modern DBs prefer this.

Instead of blocking, the DB creates multiple versions of a row.

So:

Used by:

This gives very high performance.


⭐ Isolation Levels (Important!)

Different databases allow different levels of isolation.

From weakest → strongest:

1. Read Uncommitted

Can see uncommitted data (dirty reads). Almost nobody uses this.

2. Read Committed

Can only see committed data. Most common level (Oracle default).

3. Repeatable Read

If you read a row once, you’ll see the same value again. MySQL’s default.

4. Serializable

Highest. Acts as if transactions run one-by-one. But very slow → locking or MVCC tricks.


⭐ Problems Isolation Prevents

❌ Dirty Read

You read uncommitted data of another transaction.

❌ Non-repeatable Read

You read same row twice and get different values (because someone updated in between).

❌ Phantom Read

You run the same query twice and new rows appear (someone inserted).

❌ Write Skew / Lost Updates

Two updates override each other.

Isolation prevents these problems depending on the level.


⭐ Simple Example to Make It Crystal Clear

Balance = 1000

Transaction A

Reads balance → 1000 Withdraws 500 → new balance = 500

Transaction B

Also reads balance → 1000 Withdraws 500 → new balance = 500

Final balance = 500 but both withdrew → correct result should be 0

This happens only if isolation is weak.

At proper isolation levels:

Thus: correct final balance is always 0.


⭐ Ultimate Summary (Best version)

Consistency means → data must follow rules

Isolation means → transactions don’t disturb each other

Implementation → locks, MVCC, row versions

Result → same output as if transactions ran strictly one after another


MVCC works extremely well. It is the reason modern databases like PostgreSQL, MySQL (InnoDB), Oracle, CockroachDB, and most distributed databases can handle huge concurrency without blocking.

Let me show you exactly how and why MVCC works, with a very clear explanation.


⭐ What MVCC actually does

MVCC = Multi-Version Concurrency Control

Meaning:

A database keeps multiple versions of a row so readers never block writers, and writers never block readers.

Every update creates a new version of the row instead of overwriting the old one.


⭐ How MVCC works (simple explanation)

Suppose we have this row:

id = 1
balance = 1000

Transaction A starts (T1)

Reads the row → balance = 1000

Transaction B starts (T2)

Updates the row:

UPDATE account SET balance = 500 WHERE id = 1;

In MVCC:

Version 1: balance = 1000 (old)
Version 2: balance = 500 (new)

Now how do T1 and T2 see data?

This avoids:

Each transaction sees a snapshot of the row at the time it began.


⭐ Why MVCC works (the secret)

Because MVCC never lets a read operation “look” at a row that was changed by a transaction that started after it.

Every row has hidden metadata:

So the DB knows:

Thus, each transaction gets a consistent snapshot, without blocking.


⭐ But doesn’t storing multiple versions slow down the DB?

No — because:

This is exactly why PostgreSQL and InnoDB handle millions of reads/writes per second.


⭐ What about writers?

Writers can block each other, but only when they write to the same row.

Example:

So MVCC = read-write no blocking, but write-write blocking still occurs.


⭐ A real-life example showing WHY MVCC is awesome

Imagine:

Without MVCC:

With MVCC:

This is exactly how apps like Paytm, Amazon, Swiggy handle massive concurrency.


⭐ Final Summary

MVCC works by maintaining multiple row versions.

Readers never block writers, and writers never block readers.

Each transaction sees a snapshot of data from when it started.

Multiple versions are cleaned up later (VACUUM).

This is a proven, battle-tested system used by almost all advanced databases.


⭐ Short answer:

This waiting/blocking happens entirely at the DATABASE layer. Not in Java, not in your application code.

Your Java code does nothing special.


⭐ Long answer (clear explanation)

When two transactions try to update the same row, the database enforces a write lock.

Example scenario:

T2: UPDATE account SET balance = 500 WHERE id = 1;
T3: UPDATE account SET balance = 300 WHERE id = 1;

✔ What happens internally:

  1. T2 acquires an exclusive lock on row #1 (no other transaction can modify this row)

  2. T3 attempts to update the same row → DB sees the row is already locked → DB suspends T3 (puts it in waiting state)

  3. T2 commits or rolls back → DB releases the lock

  4. T3 wakes up → continues execution → then updates the row


⭐ Who handles the waiting?

Database engine, automatically.

Your Java thread simply waits for the DB query to complete.


⭐ Example: Java code does NOT know about locks

Connection con = dataSource.getConnection();
con.setAutoCommit(false);

PreparedStatement ps = con.prepareStatement(
  "UPDATE account SET balance = ? WHERE id = 1"
);

ps.setInt(1, 500);
ps.executeUpdate();  // <--- If row is locked, this call WAITs

Notice:

The executeUpdate() call itself will block until:

Everything is handled by the DB engine.


⭐ What actually happens inside the DB?

Let’s break it down:

Step 1 — T2 updates

DB marks row #1 as:

locked_by_transaction = T2

Step 2 — T3 tries to update

DB checks:

Is row #1 locked?
Yes → by T2

So DB marks T3 as:

waiting_for = T2

Step 3 — T2 commits

DB clears lock:

locked_by_transaction = null

DB wakes up T3:

wake up T3 thread → continue

⭐ Does Java need to manage this with threads?

No. Java threads are completely unaware.

Java → sends SQL → waits Database → handles locks, waits, resume


⭐ What if Java is multithreaded?

Even then, database is the master.

Two Java threads doing:

UPDATE account SET balance = ... WHERE id = 1;

→ DB enforces locking → not your code


⭐ Which DB engines do this?

Every ACID-compliant database enforces write locks.


⭐ What about MVCC?

MVCC helps avoid read/write blocking.

But MVCC cannot prevent write/write conflicts → still needs row locks.

So:

Operation MVCC blocks?
Read vs Read ❌ No
Read vs Write ❌ No (Snapshots help)
Write vs Write ✔ Yes (lock needed)

⭐ Final Summary

✔ Write conflicts (T2 vs T3) are handled by DB

✔ Java does not implement locking

✔ SQL query automatically waits until lock is free

✔ Database ensures isolation & serializability

✔ MVCC helps reads but writes still use locks


MVCC versioning is built inside the database engine itself. You don’t enable it manually, you don’t create extra columns, and you don’t write any special logic.

The DB automatically keeps multiple versions of rows behind the scenes.

Let me explain how this works in the simplest way.


⭐ How does the DB know about versioning?

Because internally, every row has hidden metadata that you never see.

For example, in PostgreSQL each row contains:

xmin  → which transaction created this row
xmax  → which transaction deleted/updated it

In MySQL InnoDB:

transaction_id
roll_pointer (points to older version in undo log)

In Oracle:

SCN (System Change Number)
Undo segment pointer

You never see these columns — database maintains them automatically.


⭐ When an UPDATE happens, DB does NOT overwrite the row

For example:

UPDATE account SET balance = 500 WHERE id = 1;

Internally the DB does:

Step 1: Create a new row version

Row V2 (new): balance=500, xmin=T2, xmax=null

Step 2: Mark old row version as expired (logically)

Row V1 (old): balance=1000, xmin=old_txn, xmax=T2

So now DB has:

Version 1 → visible to old transactions
Version 2 → visible to new transactions

⭐ How does DB decide which version to show?

Database compares:

row.xmin <= transaction_id
AND
row.xmax is null OR > transaction_id

Meaning:

Thus, every transaction gets the right snapshot.


⭐ Do YOU need to implement versioning manually?

❌ NO

DB handles everything:

Everything is built inside the storage engine.


⭐ Do you need to enable MVCC manually?

Usually NO.

Examples:

Database MVCC On by Default? Notes
PostgreSQL ✔ Yes Pure MVCC architecture
MySQL (InnoDB) ✔ Yes Uses undo logs for versions
Oracle ✔ Yes Uses undo segments
SQL Server ➖ Partially Needs READ_COMMITTED_SNAPSHOT=ON for MVCC read consistency
CockroachDB ✔ Yes MVCC keys stored in KV layer

90% of modern DBs have MVCC enabled automatically.


⭐ Do you need to add extra columns like version, timestamp?

❌ NO — this is a different thing (Optimistic Locking for ORMs like Hibernate).

But MVCC does not require you to add such columns. Row versions are stored in internal pages/logs, not in your table schema.


⭐ So what must YOU do as a developer?

Nothing special. Use transactions normally.

Example:

BEGIN;
UPDATE account SET balance = 500 WHERE id = 1;
COMMIT;

MVCC & locks will automatically:


⭐ Final Summary

✔ MVCC versioning is fully internal to the database engine

✔ You do not configure or implement anything

✔ Every row automatically stores hidden metadata

✔ Every update automatically creates a new row version

✔ Each transaction sees the correct version based on snapshot rules

✔ DB cleans old versions later (VACUUM / purge)


👉 Even though databases implement MVCC/locking automatically, system design interviews expect you to understand the consequences, trade-offs, and when to rely on DB vs when you need to handle things yourself.

In system design, the focus is NOT:

Instead, the focus is on:

Let me break it down clearly.


⭐ Why MVCC & concurrency are important in System Design (even though DB does it automatically)

1. Because you must choose the right database

Not all databases:

Example:

In system design you must answer:

“Which DB do you choose and why?” “How will it handle concurrency?” “What isolation level do you need?”

This matters a LOT.


⭐ 2. Because isolation levels impact correctness

You must know:

Each has consequences for:

If your system needs to prevent overselling (e-commerce), you should say:

Choice: SERIALIZABLE or explicit row locking. Why: Prevents two users from ordering the same last item.

This is what interviewers want — not implementation details.


⭐ 3. Because at scale, DB concurrency becomes a bottleneck

When millions of users:

DB-level locking becomes a bottleneck.

System design requires solutions like:

Interviewers want you to know:

❌ “DB does everything” ✔ “DB becomes a bottleneck at scale, so we need to rethink design.”


⭐ 4. Because distributed systems do NOT automatically give you isolation

Example: If you store data in multiple services:

DB cannot enforce isolation across services.

You need:

This is your responsibility, not DB’s.


⭐ 5. Because interviewers want to see tradeoff thinking

For example:

“Should you use pessimistic locks or optimistic locks?” “Does MVCC solve read/write contention?” “How do we avoid write/write contention?” “What happens under high concurrency?”

Your ability to explain these decisions shows:


⭐ 6. Because NoSQL systems behave differently

MongoDB uses:

Cassandra:

So they will ask you:

“Can we use Cassandra for financial transactions?” → NO — because Cassandra does not give isolation.


⭐ 7. Because you must explain anomalies

Even if DB does MVCC, you must know:

Interviewers check if you understand why ACID matters.


⭐ FINAL SUMMARY (The REAL reason)

❌ You are NOT expected to implement MVCC.

❌ You are NOT expected to manually version rows.

✔ You ARE expected to understand how concurrency works

✔ You must know the implications for a system design

✔ You must choose DB/isolation levels smartly

✔ You must explain scalability problems when many users write same data

✔ You must know when DB is not enough and app-level logic is needed