Hello! Welcome to the first lesson in our module on Concurrency Control.
In the previous module on Query Optimization, we focused entirely on how to make a single query run as fast as possible. But a real-world database system is a bustling environment, with potentially thousands of users reading and writing data at the same time. This concurrency is essential for performance, but it also introduces a significant risk: what if two transactions interfere with each other and corrupt the data?
Today, we'll tackle this fundamental problem head-on. Our learning goal is to define serializability and identify read-write, write-read, and write-write conflicts in schedules. We'll explore why concurrency is tricky, define what it means for a concurrent execution to be "correct," and learn a practical method to check for this correctness. This is the theoretical foundation for all the practical concurrency control mechanisms we'll build in the upcoming lessons.
The Problem with Concurrency
Imagine two people with a shared bank account trying to withdraw money at the exact same time. It's easy to see how things could go wrong, leading to an incorrect final balance. This is the core challenge of concurrency control.
To get a solid grasp of the problems we need to solve, let's start with a video from CMU's Intro to Database Systems course. It uses simple examples to illustrate why simply interleaving operations can lead to chaos.
#16 - Concurrency Control Theory ✸ Firebolt Database Talk (CMU Intro to Database Systems)
This clip from Andy Pavlo's lecture clearly motivates the need for concurrency control. It shows how simultaneous transactions can lead to data corruption and discusses why the simplest solution—running everything one-by-one—is not sufficient for high-performance systems.
Please watch from 02:27 to 12:34. Pay attention to the two motivating examples: a crash during an update and two simultaneous withdrawals. Note the discussion of the 'strawman' system and why we need a more sophisticated approach to allow for parallelism while ensuring correctness.
As the video highlights, we want to maximize parallelism by interleaving operations from different transactions. However, we need a formal way to ensure that this interleaving doesn't lead to an incorrect state. This brings us to the concept of serializability.
Defining Correctness: Schedules and Serializability
To reason about concurrency, we need some precise terminology.
- A Transaction is a sequence of read and write operations that are treated as a single, atomic unit of work. For example, transferring money involves reading the source account, writing the new balance, reading the destination account, and writing its new balance.
- A Schedule is a sequence of operations from one or more transactions as executed by the database. When transactions run concurrently, their operations are interleaved in the schedule.
- A Serial Schedule is a schedule where transactions are executed one after another, with no interleaving. This is simple and guaranteed to be correct, but it offers no parallelism.
The goal of a concurrency control protocol is to create an interleaved schedule that is "as good as" a serial one. This leads to our core definition:
A schedule is Serializable if it is equivalent in its outcome to some serial schedule.
This is the gold standard for isolation. It gives us the best of both worlds: we get the performance of interleaving operations, with the correctness guarantee of a serial execution. The database may run transactions T1 and T2 concurrently, but the final state will be as if either T1 ran before T2, or T2 ran before T1.
For a more formal and visual explanation of these terms, the following short video provides excellent, clear definitions.
Transactions - Schedules and Serializability | Database Tutorial 7b
This video provides concise, formal definitions for schedules, serial schedules, and serializability. It's a great way to solidify your understanding of these core concepts.
Watch from the beginning to 06:04. Focus on the distinction between a general schedule and a serial schedule, and the definition of a serializable schedule as one whose effect is the same as some serial schedule.
The concept of serializability is the 'I'—Isolation—in the well-known ACID (Atomicity, Consistency, Isolation, Durability) properties that guarantee reliable transaction processing. Complete isolation is equivalent to serializability.
When Operations Collide: Conflicts
To understand what makes a schedule non-serializable, we need to identify the specific interactions between operations that can cause problems. These are called conflicts.
Two operations from different transactions conflict if they access the same data object, and at least one of the operations is a write. This gives us three types of conflicts:
- Read-Write (R-W) Conflict: Transaction T1 reads an object, and then T2 writes to the same object. This can cause an unrepeatable read, where T1 might get a different value if it were to read the same object again.
- Write-Read (W-R) Conflict: Transaction T1 writes an object, and then T2 reads it. If T1 has not yet committed, this can cause a dirty read, where T2 reads a value that may be rolled back later.
- Write-Write (W-W) Conflict: Transaction T1 writes an object, and then T2 also writes to it. This can cause a lost update, where T1's change is overwritten by T2 before anyone has had a chance to see it.
Note that a Read-Read (R-R) conflict does not exist, as two transactions reading the same data do not interfere with each other.
The following video segment provides clear, illustrated examples for each of these conflict types and the anomalies they cause.
#16 - Concurrency Control Theory ✸ Firebolt Database Talk (CMU Intro to Database Systems)
This clip details the three fundamental conflict types and the specific anomalies they cause. The examples make it very clear how interleaved operations can go wrong.
Watch from 39:32 to 44:30. For each of the three conflict types (read-write, write-read, write-write), make sure you understand the corresponding anomaly (unrepeatable read, dirty read, lost update).
A Practical Test: Conflict Serializability
We now know what serializability is and what conflicts can break it. But how can we algorithmically determine if a given schedule is serializable? We do this by testing for a slightly stricter, but much more practical, form of serializability called Conflict Serializability.
A schedule is conflict serializable if it can be transformed into a serial schedule by a series of swaps of adjacent, non-conflicting operations.
While this definition is formal, checking it by swapping operations is tedious. A much more elegant and efficient method is to use a Dependency Graph (also called a Precedence Graph).
Here’s the algorithm:
- Create a node in the graph for each transaction in the schedule (e.g., T1, T2, T3).
- Draw a directed edge from Ti to Tj if an operation in Ti conflicts with a subsequent operation in Tj. You scan the schedule and for every pair of conflicting operations, you draw an edge from the transaction that came first to the transaction that came second.
The rule is simple: A schedule is conflict serializable if and only if its dependency graph is acyclic (contains no cycles).
A cycle in the graph (e.g., T1 → T2 → T1) means there's a circular dependency, which is impossible to resolve in any serial order. If the graph is acyclic, a valid serial order can be found by performing a topological sort of the graph.

Let's watch the CMU video again for a walkthrough of how to build and interpret these graphs.
#16 - Concurrency Control Theory ✸ Firebolt Database Talk (CMU Intro to Database Systems)
This is the key technique of the lesson. The video explains how to build a dependency graph and use it to test for conflict serializability by checking for cycles. This is the practical algorithm you'll use.
Watch from 46:49 to 55:18. Focus on how edges are added to the graph based on conflicting operations in the schedule. Understand why a cycle means the schedule is not conflict serializable, and an acyclic graph is.
Test your understanding!
Consider the following schedule S with three transactions: T1, T2, and T3. R(X) means read object X, and W(X) means write object X.
Schedule S:T1: R(A)T2: W(A)T1: W(B)T3: W(A)T2: W(B)T3: W(B)
- Identify all the conflicts in this schedule.
- Draw the dependency graph for schedule S.
- Is this schedule conflict serializable? If so, provide one valid serial execution order.
Show answer
-
Conflicts:
- T1: R(A) vs T2: W(A) (Edge: T1 → T2)
- T1: R(A) vs T3: W(A) (Edge: T1 → T3)
- T2: W(A) vs T3: W(A) (Edge: T2 → T3)
- T1: W(B) vs T2: W(B) (Edge: T1 → T2 - duplicate)
- T1: W(B) vs T3: W(B) (Edge: T1 → T3 - duplicate)
- T2: W(B) vs T3: W(B) (Edge: T2 → T3 - duplicate)
-
Dependency Graph:
The graph has three nodes: T1, T2, T3.
Based on the conflicts, the edges are:- An edge from T1 to T2.
- An edge from T1 to T3.
- An edge from T2 to T3.
-
Is it conflict serializable?
Yes. The dependency graph (T1 → T2 → T3, and T1 → T3) is a Directed Acyclic Graph (DAG). There are no cycles.Valid Serial Order:
A topological sort of the graph gives a valid serial order. T1 must come first. After T1, T2 must come before T3. Therefore, the only valid serial order is T1, T2, T3.
Conclusion
In this lesson, we took our first step into the world of database concurrency. We established that while interleaving transactions is crucial for performance, it opens the door to dangerous anomalies.
Key Takeaways:
- Concurrent execution can lead to anomalies like dirty reads, unrepeatable reads, and lost updates if not managed correctly.
- The gold standard for correctness is serializability, which guarantees that a concurrent schedule has the same outcome as some one-at-a-time (serial) execution.
- Operations from different transactions conflict if they touch the same data item and at least one is a write. There are three types: Read-Write, Write-Read, and Write-Write.
- We can efficiently test for conflict serializability by constructing a dependency graph and checking for cycles. An acyclic graph implies the schedule is correct.
Preview of the Next Lesson
We now have a robust theoretical tool to determine if a schedule is correct. But this is an after-the-fact analysis. How does a database enforce a serializable schedule as transactions are running? One of the oldest and most common methods is by using locks.
In our next lesson, we will begin our journey into practical implementation by learning how to implement a lock manager that supports shared (read) and exclusive (write) locks. This will be the foundational component for building our first concurrency control protocol.