Hello! In our last lesson, we tackled deadlocks, a critical problem in lock-based concurrency control like Strict 2PL. We saw that while S2PL provides strong consistency guarantees, its pessimistic nature—where transactions must acquire locks and wait—can lead to blocking and reduced throughput.
Today, we shift gears to explore a fundamentally different and more optimistic approach: Multi-Version Concurrency Control (MVCC). This is the mechanism behind the high performance of many modern databases, including PostgreSQL and MySQL (InnoDB). Our goal is to understand how MVCC improves concurrency, particularly for read-heavy workloads. We'll see how it elegantly solves the problem of readers blocking writers and vice-versa.
From Pessimistic Locking to Optimistic Versioning
In our previous lessons on two-phase locking, the core philosophy was pessimistic: assume conflicts are likely, so lock resources to prevent them. This leads to a world of waiting.
MVCC operates on an optimistic philosophy: assume conflicts are rare, so let transactions proceed without waiting. Instead of locking and overwriting data, MVCC creates a new version of a data item every time it's updated.
This single change has a profound impact:
- Writers don't block readers: A reader can access an older, consistent version of the data while a writer is simultaneously creating a new one.
- Readers don't block writers: A reader never acquires a lock on the data it's reading, so a writer is free to update it.
The following image provides a high-level comparison between the two approaches, contrasting the blocking nature of 2PL with the non-blocking read model of MVCC.

To get a clear, practical introduction to this concept, let's start with a couple of short readings from technical blogs. They do an excellent job of framing the problem and the MVCC solution.
PostgreSQL MVCC: The Secret Behind Its Powerful ...
First, read this introduction from a blog post on PostgreSQL's MVCC. It clearly explains the problem with traditional locking and how MVCC addresses it.
Read the first three sections: 'What is MVCC in PostgreSQL?', 'The Problem: Locking and Blocking', and 'The MVCC Solution'.
Multiversion Concurrency Control (MVCC): A Practical ...
Next, this glossary entry from Celerdata provides a concise comparison between MVCC and locking-based systems. The table format is particularly useful for contrasting the two.
Read the section 'Comparing MVCC with Locking-Based Concurrency Control', focusing on the tables that compare Data Reads, Data Writes, and Concurrency/Throughput.
These readings establish the core benefit of MVCC: it achieves higher concurrency by avoiding the contention between readers and writers that plagues locking-based systems.
How it Works: Snapshots and Visibility Rules
If multiple versions of a row exist, how does a transaction know which one to read? The answer lies in the concept of a database snapshot.
When a transaction begins, it is essentially given a "snapshot" of the database frozen at that moment in time. Throughout its entire duration, the transaction will only see data that was committed before it started. Any changes made by other concurrent transactions will be invisible to it.
To implement this, the database attaches metadata to every row version. Let's see how this works in practice.
18 - Multi-Version Concurrency Control MVCC (CMU Intro to Database Systems / Fall 2022)
This clip from CMU's Intro to Database Systems course introduces the core idea of MVCC and walks through a concrete example using timestamps to manage version visibility.
Watch from 29:09 to 40:55. Pay close attention to: The core idea: maintaining multiple physical versions for one logical object. The use of begin and end timestamps on each version to define its 'lifetime'. How a transaction uses its own start time to decide which version of a tuple is visible to it, ensuring repeatable reads.
As the video explains, each row version has metadata indicating the transaction that created it and the transaction that "expired" it. A transaction can see a version if that version was created by a committed transaction before began, and was not expired until after began.
This connects directly to your systems background. In a system like PostgreSQL, this is implemented with two hidden columns in every table:
xmin: The ID of the transaction that created this row version.xmax: The ID of the transaction that deleted or updated this row version.
When your transaction (say, with ID txid_current) runs a SELECT, the database applies these visibility rules to every row version it encounters:
A row version is visible if xmin is a committed transaction ID less than txid_current, AND xmax is either NULL or belongs to a transaction that had not yet committed when txid_current began.
This simple but powerful mechanism ensures that each transaction operates on its own consistent, isolated snapshot of the database.
PostgreSQL MVCC: The Secret Behind Its Powerful ...
For a concrete look at this implementation, this section of the PostgreSQL blog post details the lifecycle of a transaction and the role of xmin and xmax.
Read the sections from 'MVCC Lifecycle in PostgreSQL' down to 'Visibility to Other Transactions'. This provides an excellent, applied view of the concepts from the video.
A Note on Version Storage
From an engineering perspective, you might wonder where these different versions are stored. There are three main strategies, each with different performance trade-offs:
- Append-Only Storage: New versions are added directly into the same table space as the old versions. This is what PostgreSQL does. It's simple, but it can lead to table bloat and requires a "garbage collection" process (called
VACUUMin PostgreSQL) to clean up old, invisible versions. - Time-Travel Storage: Old versions are moved to a separate "time-travel" or "history" table. This keeps the main table clean with only the latest versions, but requires traversing to another table to find older versions.
- Delta Storage: The main table stores the newest version. When a row is updated, only the changes (the "delta") are stored in a separate undo log or rollback segment. To reconstruct an old version, the database applies these deltas backward from the current version. This is the approach used by Oracle and MySQL's InnoDB.
18 - Multi-Version Concurrency Control MVCC (CMU Intro to Database Systems / Fall 2022)
The CMU lecture also covers these three storage mechanisms. This part is great for appreciating the systems-level design choices involved.
Watch from 52:03 to 01:03:38. Try to understand the basic trade-offs between the append-only, time-travel, and delta storage models.
The Payoff: Why MVCC Excels for Read-Heavy Workloads
Now we can tie everything together. Why is this versioning mechanism so beneficial for applications with many concurrent reads?
Consider a typical web application or a business intelligence dashboard. These are classic read-heavy workloads: many users are reading data, while writes are less frequent but still happening.
- Long-running reports: An analyst runs a complex query that takes minutes to complete. With MVCC, this query operates on its snapshot. It does not block any incoming user updates, nor is it affected by them. In a locking system, this query might hold shared locks for minutes, grinding the application to a halt.
- High-traffic websites: Thousands of users are browsing product catalogs (reading data) while inventory levels are being updated by purchases (writing data). MVCC allows these reads and writes to happen simultaneously without contention, ensuring a fast user experience.
This resource clearly articulates the scenarios where MVCC is the ideal choice.
Multiversion Concurrency Control (MVCC): A Practical ...
This section of the Celerdata article explicitly discusses the use cases where MVCC shines, directly addressing our learning outcome.
Read the part titled 'MVCC Is Ideal When...'. This connects all the mechanisms we've discussed to real-world performance benefits.
Test your understanding!
Imagine a database for an e-commerce site. A long-running analytics query (T_report) starts at 9:00 AM to calculate the total sales for the past week. At 9:01 AM, a customer buys an item, and transaction T_purchase updates the quantity of that item in the products table.
- In a system using Strict 2PL, what potential issue could arise between
T_reportandT_purchase? - In a system using MVCC, describe what each transaction sees and whether they block each other.
Show answer
- Strict 2PL:
T_reportwould likely acquire a shared lock on theproductstable or many of its rows. WhenT_purchasetries to acquire an exclusive lock to update the quantity, it would be blocked and have to wait untilT_reportcompletes and releases its shared locks. This could take minutes, leaving the customer's transaction hanging. - MVCC:
T_reportoperates on a snapshot of the database from 9:00 AM.T_purchasecreates a new version of the product row with the updated quantity.T_reportcontinues to read the old version that existed at 9:00 AM, completely unaware of the purchase. Neither transaction blocks the other. The system remains highly responsive.
Conclusion
Today, we've explored the elegant and powerful model of Multi-Version Concurrency Control. By moving from a "lock and wait" model to a "version and proceed" model, MVCC provides a huge boost to concurrency, especially in scenarios dominated by read operations.
Key Takeaways:
- MVCC is an optimistic concurrency control method where updates create new versions of data instead of overwriting existing data.
- The core benefit is that readers don't block writers, and writers don't block readers, leading to much higher concurrency than locking-based schemes.
- Transactions operate on a consistent snapshot of the database taken at their start time.
- Visibility is determined by metadata on each row version (like
xmin/xmax), which allows a transaction to see only the appropriate versions for its snapshot. - This makes MVCC exceptionally well-suited for read-heavy workloads, such as web applications and analytics systems, where it prevents long-running reads from blocking critical writes.
Preview of the Next Lesson
The consistent view of the database that a transaction gets under MVCC is a powerful isolation guarantee known as Snapshot Isolation. While it prevents many common anomalies, it is subtly different from and slightly weaker than true SERIALIZABLE isolation. In our next lesson, we will formally describe the mechanism of snapshot isolation and explore some of the rare but interesting anomalies, like write skew, that it can permit.