Hello! Welcome to your next lesson in the "Database Architecture & Design" module.
In our previous lesson on sharding, we saw how challenging it is to maintain transactional guarantees across a distributed database. This naturally leads to a follow-up question: how do databases handle transactions and concurrency within a single node? When multiple users or processes try to read and write data simultaneously, how does the database prevent chaos while still offering good performance?
This lesson will explore the core mechanism that relational databases use to manage this balancing act. Your learning outcome is to describe the purpose and trade-offs of different transaction isolation levels. We'll unpack what "isolation" truly means in the context of ACID, investigate the subtle concurrency bugs it prevents, and analyze the different "levels" you can choose from, each offering a different trade-off between data consistency and system performance. This is a fundamental concept for any software architect designing reliable systems.
1. The Core Problem: Concurrent Transactions Interfere
A database transaction is a sequence of operations performed as a single logical unit of work. All operations in a transaction must either succeed entirely or be rolled back, leaving the database unchanged. This is the "Atomicity" in ACID.
When many transactions run at the same time (concurrently), they can interfere with each other in unexpected ways, leading to data corruption or incorrect reads. The "Isolation" property of ACID is designed to prevent this interference. Ideally, each transaction should feel as though it is the only thing running on the database. In reality, achieving perfect isolation can be slow. Therefore, the SQL standard defines several isolation levels, which allow you to relax the rules in exchange for better performance.
To understand why these levels are necessary, we first need to understand the problemsโor "read phenomena"โthat can occur.
Relational Database ACID Transactions (Explained by Example)
Let's start by exploring these concurrency problems. The following video from Hussein Nasser provides clear, practical examples of the three most common anomalies.
Watch the video from 07:10 to 18:59. This segment defines isolation and then walks through detailed examples of: Dirty Reads (starts at 11:34) Non-Repeatable Reads (starts at 14:48) Phantom Reads (starts at 16:49) Focus on understanding the sequence of events that leads to each anomaly.
To summarize the key anomalies:
- Dirty Read: A transaction reads data that has been modified by another transaction that has not yet committed. If the other transaction rolls back, the first transaction has read a value that never officially existed.
- Non-Repeatable Read: A transaction reads the same row twice but gets different values because another transaction updated that row and committed the change in between the two reads.
- Phantom Read: A transaction runs the same query twice but gets a different set of rows because another transaction inserted or deleted rows that match the query's criteria.
Understanding these anomalies is the key to understanding the purpose of isolation levels. Each level is defined by which of these phenomena it prevents.
2. The Solution: The Four Standard Isolation Levels
The ANSI SQL standard defines four isolation levels that act as a control knob for the trade-off between consistency and performance. A stricter level prevents more anomalies but typically requires more database resources (like locking), which can reduce concurrency and slow down the system.

Let's walk through these levels, from weakest to strongest.
Understanding Database Isolation Levels: A Developer's Guide to Consistent Transactions
This article from dev.to provides a very concise summary of the four levels and their guarantees. The table is an excellent reference.
Read the sections 'ANSI SQL Isolation Levels' and 'How Isolation Levels Work in Different Databases'. Pay close attention to the first table, which summarizes what each level allows and prevents.
Here is a breakdown of the levels:
-
Read Uncommitted:
- Guarantees: The lowest level. It offers virtually no isolation.
- Anomalies Prevented: None. It allows Dirty Reads, Non-Repeatable Reads, and Phantom Reads.
- Use Case: Very rare. Sometimes used for statistical queries on large datasets where precision is not critical and performance is paramount.
-
Read Committed:
- Guarantees: A transaction will only ever see data that has been committed.
- Anomalies Prevented: Prevents Dirty Reads.
- Anomalies Allowed: Non-Repeatable Reads and Phantom Reads can still occur.
- Use Case: This is the default level for many popular databases (PostgreSQL, Oracle, SQL Server). It's a good balance, ensuring you don't read "garbage" data while still allowing high concurrency.
-
Repeatable Read:
- Guarantees: Once a transaction reads a row, it is guaranteed to see the exact same data if it reads that row again, for the entire duration of the transaction.
- Anomalies Prevented: Prevents Dirty Reads and Non-Repeatable Reads.
- Anomalies Allowed: Phantom Reads are still possible according to the SQL standard, though some databases like MySQL's InnoDB engine also prevent them at this level.
- Use Case: The default for MySQL. Useful when a transaction needs to read the same data multiple times and requires it to be stable.
-
Serializable:
- Guarantees: The highest level of isolation. It ensures that executing transactions concurrently has the same effect as if they were executed serially (one after another).
- Anomalies Prevented: Prevents Dirty Reads, Non-Repeatable Reads, and Phantom Reads.
- Use Case: Required for applications where data consistency is absolutely critical and no anomalies can be tolerated (e.g., financial systems, accounting ledgers). This comes at the cost of the lowest concurrency.
Test your understanding!
You are building an e-commerce application. A long-running analytics job needs to generate a sales report. The job first queries all sales to calculate total revenue, then, in the same transaction, it queries the same sales data again to categorize sales by product type.
While the job is running, a customer successfully purchases an item, which updates a sales record.
Which is the lowest isolation level you can set for the analytics job to guarantee that both the total revenue calculation and the category breakdown are based on the exact same set of data?
Show answer
The lowest level required is Repeatable Read.
Here's why:
- The problem describes a scenario where the same data is read twice within one transaction, and an external
UPDATEoccurs in between. This is the definition of a Non-Repeatable Read. - Read Committed would not be sufficient because it allows Non-Repeatable Reads. The analytics job might see the updated sales record on its second query, leading to an inconsistent report.
- Repeatable Read specifically prevents Non-Repeatable Reads, ensuring that the second query sees the exact same data as the first, guaranteeing a consistent report.
- Serializable would also work, but the question asks for the lowest level that provides the necessary guarantee.
3. An Architect's View: Implementation Trade-offs and Subtle Flaws
Simply knowing the four levels isn't enough for an architectural mindset. You also need to understand how they are implemented and what subtle problems can still arise.
Databases primarily use two mechanisms to achieve isolation:
- Pessimistic Locking (Two-Phase Locking - 2PL): This approach assumes conflicts are likely. When a transaction reads or writes data, it acquires locks that can prevent other transactions from accessing that same data. This can lead to transactions waiting on each other and potentially causing deadlocks.
- Optimistic Concurrency Control (MVCC - Multi-Version Concurrency Control): This approach assumes conflicts are rare. Instead of locking, the database maintains multiple versions of a row. When a transaction starts, it's given a "snapshot" of the database at that point in time. It reads from this consistent snapshot, and write operations don't block reads. If two transactions try to modify the same data, one will typically be aborted and have to retry. Postgres and Oracle heavily use MVCC.
The choice of mechanism has significant performance implications. Furthermore, even seemingly strong isolation levels have subtle failure modes.
"Transactions: myths, surprises and opportunities" by Martin Kleppmann
Let's dive deeper with Martin Kleppmann, a leading expert on data systems. This video uncovers the subtle but critical race conditions that can still occur even with 'strong' isolation, and discusses how serializable isolation is actually implemented.
This segment is more advanced but essential for a deep understanding. Watch from 14:49 to 27:38. Read Skew (14:49 - 18:00): Understand this anomaly that can happen under Read Committed and how Snapshot Isolation (a form of MVCC) prevents it. Write Skew (18:00 - 21:53): Pay close attention to this critical anomaly. It shows why even Snapshot Isolation/Repeatable Read isn't always enough, leading to the need for true Serializable isolation. Implementing Serializable (21:53 - 27:38): Learn about the different ways databases provide the strongest guarantee: strict locking (2PL), optimistic approaches (SSI), and even actual serial execution.
The key takeaways from this advanced section are:
- Snapshot Isolation (the mechanism behind Repeatable Read in Postgres) is powerful but not perfect. It is vulnerable to Write Skew, where two transactions read the same data, make decisions based on it, and write to different rows, creating an inconsistent state without directly conflicting.
- True Serializable isolation is the only way to prevent all such anomalies.
- The implementation of
Serializablevaries. SQL Server historically used strict Two-Phase Locking (pessimistic), which can cause deadlocks. Postgres uses Serializable Snapshot Isolation (SSI) (optimistic), which aborts transactions upon detecting a potential conflict.
This detailed article provides an exhaustive comparison of how different major databases (PostgreSQL, MySQL, SQL Server, Oracle) implement each level and handle these anomalies. It's an excellent resource for seeing the real-world differences. Feel free to bookmark it for future reference: Transaction isolation levels in PostgreSQL, MySQL, SQL...
Conclusion
You now have a comprehensive understanding of database transaction isolation levels, a critical concept in database architecture. You can describe not only the "what" (the four levels and the anomalies they prevent) but also the "why" (the consistency vs. performance trade-off) and the "how" (locking vs. MVCC and their implications).
Key Takeaways:
- Purpose: Isolation levels are a crucial mechanism for controlling how concurrent transactions interact, managing the trade-off between data consistency and system performance.
- Anomalies: The standard defines three key read phenomena to prevent: Dirty Reads, Non-Repeatable Reads, and Phantom Reads. More subtle bugs like Write Skew also exist.
- The Four Levels:
Read Uncommitted,Read Committed,Repeatable Read, andSerializableoffer a graduating scale of protection at the cost of concurrency. - Trade-offs: Choosing an isolation level is a critical architectural decision. Stricter levels prevent more bugs but can lead to performance issues like blocking, deadlocks, or transaction aborts. The golden rule is to use the lowest isolation level that still guarantees the correctness of your application's logic.
- Implementation Matters: Different databases use different default levels and underlying mechanisms (locking vs. MVCC), leading to different performance characteristics and failure modes.
Preview of the Next Lesson:
With this firm grasp on how relational databases ensure data consistency, we are now perfectly positioned to compare them against their NoSQL counterparts. In our next lesson, we will design a basic data model for a given application, choosing between SQL and NoSQL. Your understanding of transactional guarantees will be a key factor in making an informed architectural decision.