Loading learning content...
All our theoretical groundwork—schedules, serial schedules, equivalence, conflicts—culminates in this page. The precedence graph (also called serializability graph or serialization graph) transforms the abstract question "Is this schedule conflict-serializable?" into a concrete, efficient algorithm.
The precedence graph method is:
This is the standard algorithm taught in every database course and serves as the foundation for understanding how real concurrency control protocols work. Whether you're analyzing schedules manually, implementing a database scheduler, or understanding isolation levels, the precedence graph is your essential tool.
By the end of this page, you will be able to construct a precedence graph from any schedule, detect cycles using DFS or other methods, extract an equivalent serial order via topological sort when serializable, and analyze various schedules to determine their serializability status with complete confidence.
Given a schedule S over transactions T = {T₁, T₂, ..., Tₙ}, the precedence graph G(S) = (V, E) is a directed graph where:
Vertices (V): One vertex for each transaction Tᵢ in S
Edges (E): A directed edge Tᵢ → Tⱼ exists if and only if there is a conflict between Tᵢ and Tⱼ where Tᵢ's operation appears before Tⱼ's operation in S
(Tᵢ → Tⱼ) ∈ E ⟺ ∃ conflicting operations oᵢ ∈ Tᵢ, oⱼ ∈ Tⱼ
such that oᵢ <S oⱼ (oᵢ precedes oⱼ in S)
Each edge represents an ordering constraint:
Why? Because there's a conflict where Tᵢ's operation happens first. In any equivalent serial schedule, this ordering must be preserved, which means Tᵢ must complete entirely before Tⱼ starts.
Edges arise from three types of conflicts:
| Conflict | Operations | Edge Meaning |
|---|---|---|
| RW | rᵢ(X) <S wⱼ(X) | Tᵢ reads before Tⱼ writes |
| WR | wᵢ(X) <S rⱼ(X) | Tⱼ reads Tᵢ's value |
| WW | wᵢ(X) <S wⱼ(X) | Tⱼ's write overwrites Tᵢ's |
All three create the same type of edge: Tᵢ → Tⱼ
Multiple conflicts between the same pair: If there are multiple conflicts between Tᵢ and Tⱼ (e.g., on different data items), they might create edges in the same direction (Tᵢ → Tⱼ) or opposite directions (Tᵢ → Tⱼ and Tⱼ → Tᵢ). Opposite-direction edges between the same pair form a cycle of length 2.
Multiple conflicts creating the same edge (e.g., two RW conflicts both giving T₁ → T₂) don't result in multiple edges. We only care whether an edge exists, not how many conflicts created it. The precedence graph is a simple directed graph, not a multigraph.
Algorithm: ConstructPrecedenceGraph(S)
Input: Schedule S over transactions T = {T₁, T₂, ..., Tₙ}
Output: Precedence graph G = (V, E)
1. Initialize G:
- V = {T₁, T₂, ..., Tₙ} // One vertex per transaction
- E = ∅ // Empty edge set initially
2. For each data item X accessed in S:
a. Collect all operations on X in schedule order
b. For each pair of operations (oᵢ, oⱼ) on X where:
- oᵢ ∈ Tᵢ and oⱼ ∈ Tⱼ with i ≠ j
- oᵢ appears before oⱼ in S
- At least one of oᵢ, oⱼ is a write
Add edge Tᵢ → Tⱼ to E (if not already present)
3. Return G = (V, E)
Schedule S:
r₁(A) r₂(A) w₂(A) w₁(A) r₁(B) r₂(B) w₂(B) w₁(B)
Positions:
1: r₁(A) 2: r₂(A) 3: w₂(A) 4: w₁(A)
5: r₁(B) 6: r₂(B) 7: w₂(B) 8: w₁(B)Step 1: Initialize
- V = {T₁, T₂}
- E = ∅
Step 2a: Data item A
Operations: r₁(A)@1, r₂(A)@2, w₂(A)@3, w₁(A)@4
Conflict pairs:
- r₁(A)@1, w₂(A)@3: RW, T₁ before T₂ → add T₁ → T₂
- r₂(A)@2, w₁(A)@4: RW, T₂ before T₁ → add T₂ → T₁
- w₂(A)@3, w₁(A)@4: WW, T₂ before T₁ → T₂ → T₁ (already added)
Step 2b: Data item B
Operations: r₁(B)@5, r₂(B)@6, w₂(B)@7, w₁(B)@8
Conflict pairs:
- r₁(B)@5, w₂(B)@7: RW, T₁ before T₂ → T₁ → T₂ (already added)
- r₂(B)@6, w₁(B)@8: RW, T₂ before T₁ → T₂ → T₁ (already added)
- w₂(B)@7, w₁(B)@8: WW, T₂ before T₁ → T₂ → T₁ (already added)
Final Graph:
T₁ ⟷ T₂ (bidirectional = cycle!)
Edges: E = {T₁ → T₂, T₂ → T₁}The graph has edges in both directions between T₁ and T₂. This is a cycle of length 2. The schedule is NOT conflict-serializable because there's no way to order T₁ and T₂ consistently.
Processing conflicts data-item by data-item is systematic and reduces errors. For each data item, list all operations in schedule order, then check every pair involving at least one write. This ensures you don't miss any conflicts.
Theorem (Conflict Serializability Test):
A schedule S is conflict-serializable if and only if its precedence graph G(S) is acyclic.
S is conflict-serializable ⟺ G(S) is acyclic
If direction (Acyclic → Serializable):
If G(S) is acyclic, it has a topological ordering T_{π(1)}, T_{π(2)}, ..., T_{π(n)}. This ordering respects all edges: if Tᵢ → Tⱼ, then Tᵢ appears before Tⱼ.
Construct serial schedule Ss = T_{π(1)} · T_{π(2)} · ... · T_{π(n)}.
For every conflict oᵢ <S oⱼ in S, we have edge Tᵢ → Tⱼ in G(S), which means Tᵢ comes before Tⱼ in the topological order, so oᵢ <Ss oⱼ in Ss.
Thus S ≡c Ss (all conflicts have same ordering). Therefore S is conflict-serializable.
Only if direction (Serializable → Acyclic):
Suppose G(S) has a cycle: Tᵢ₁ → Tᵢ₂ → ... → Tᵢₖ → Tᵢ₁
This means:
By transitivity: Tᵢ₁ must precede itself. Contradiction!
No serial schedule can satisfy all constraints, so S is not conflict-serializable.
To test conflict serializability: (1) Construct the precedence graph G(S), (2) Check if G(S) is acyclic. If acyclic → serializable, find equivalent serial order via topological sort. If cyclic → not serializable, the cycle shows the contradiction.
| Graph Status | Serializability | Action |
|---|---|---|
| Acyclic | Conflict-serializable ✓ | Topological sort gives serial order |
| Cyclic | Not conflict-serializable ✗ | Cycle shows why |
The standard algorithm for cycle detection in directed graphs:
Algorithm: HasCycle(G)
Input: Directed graph G = (V, E)
Output: True if G has a cycle, False otherwise
1. color[v] = WHITE for all v ∈ V
// WHITE = unvisited, GRAY = in progress, BLACK = finished
2. For each vertex v ∈ V:
If color[v] = WHITE:
If DFS-Visit(v) returns True:
Return True // Cycle found
3. Return False // No cycle
DFS-Visit(u):
1. color[u] = GRAY
2. For each neighbor v of u:
If color[v] = GRAY:
Return True // Back edge = cycle!
If color[v] = WHITE:
If DFS-Visit(v) returns True:
Return True
3. color[u] = BLACK
4. Return False
Complexity: O(V + E) = O(n + edges) where edges ≤ n² for n transactions
Trying to compute a topological sort also detects cycles:
Algorithm: TopologicalSort(G)
Input: Directed graph G = (V, E)
Output: Topological ordering if acyclic, CYCLE if cyclic
1. Compute in-degree for each vertex
2. Queue = all vertices with in-degree 0
3. result = empty list
4. While Queue is not empty:
a. u = dequeue
b. Append u to result
c. For each edge (u, v):
- Decrement in-degree[v]
- If in-degree[v] = 0:
Enqueue v
5. If |result| = |V|:
Return result // Valid topological order
Else:
Return CYCLE // Some vertices unreachable = cycle exists
Complexity: O(V + E)
For small precedence graphs, check for bidirectional edges:
For each pair (Tᵢ, Tⱼ):
If edge Tᵢ → Tⱼ exists AND edge Tⱼ → Tᵢ exists:
Return CYCLE
This catches the most common case: two transactions with conflicts in both directions.
For exam/academic purposes, visual inspection often suffices for small graphs. Look for: (1) bidirectional edges (immediate 2-cycle), (2) any path from a vertex back to itself. For implementation, DFS-based detection or Kahn's topological sort algorithm are both O(V + E) and straightforward.
When the precedence graph is acyclic, we can find an equivalent serial schedule using topological sort.
A topological sort of a directed acyclic graph (DAG) is a linear ordering of vertices such that for every edge (u, v), u appears before v in the ordering.
Key Insight: For an acyclic precedence graph, any topological sort gives a valid equivalent serial schedule.
If the graph has multiple valid topological sorts, all of them are equivalent serial schedules!
Graph: T₁ → T₃, T₂ → T₃
Valid topological sorts:
- T₁, T₂, T₃ ✓
- T₂, T₁, T₃ ✓
Both are valid serial schedules equivalent to the original.
S: r₂(A) r₁(B) w₂(A) r₃(A) w₁(B) w₃(A)
Transactions: T₁, T₂, T₃Step 1: Construct Precedence Graph
Data item A: r₂(A)@1, w₂(A)@3, r₃(A)@4, w₃(A)@6
- r₂(A)@1, w₃(A)@6: RW → T₂ → T₃
- w₂(A)@3, r₃(A)@4: WR → T₂ → T₃ (already)
- w₂(A)@3, w₃(A)@6: WW → T₂ → T₃ (already)
Data item B: r₁(B)@2, w₁(B)@5
- Only T₁ operations → No inter-transaction conflicts
Graph edges: E = {T₂ → T₃}
Vertices: {T₁, T₂, T₃}
Step 2: Check for cycles
- No edge into T₁
- No edge into T₂
- Only T₂ → T₃ edge
- No cycles!
Step 3: Topological sort
In-degrees: T₁=0, T₂=0, T₃=1
Start with T₁ and T₂ (both have in-degree 0)
Possible orders:
- T₁, T₂, T₃ ✓
- T₂, T₁, T₃ ✓
- T₂, T₃, T₁ ✗ (T₃ must come after T₂)
Wait... T₁ has no constraints with T₂ or T₃!
Actually valid orders:
- T₁, T₂, T₃
- T₂, T₁, T₃
- T₂, T₃, T₁
- T₁, T₂, T₃ (where T₁ can go anywhere)
Let's verify: T₂, T₃, T₁
- T₂ → T₃ satisfied ✓
- No constraints on T₁ ✓
All these are valid equivalent serial schedules!When a transaction has no edges (T₁ in this case), it can appear anywhere in the serial order. The only constraint is T₂ → T₃, so T₂ must precede T₃. T₁ is independent and can be placed anywhere.
Transactions with no edges in the precedence graph (no conflicts with other transactions) can be placed anywhere in the serial order. They access completely disjoint data sets from other transactions and are truly independent.
Let's work through several complete examples to solidify your understanding.
S₁: r₁(A) w₁(A) r₂(A) w₂(A) r₂(B) w₂(B) r₃(B) w₃(B)
Transactions: T₁, T₂, T₃
Data items: A, BConflict Analysis:
Data item A: r₁(A), w₁(A), r₂(A), w₂(A)
- w₁(A) → r₂(A): WR → T₁ → T₂
- w₁(A) → w₂(A): WW → T₁ → T₂ (redundant)
- r₁(A) → w₂(A): RW → T₁ → T₂ (redundant)
Data item B: r₂(B), w₂(B), r₃(B), w₃(B)
- w₂(B) → r₃(B): WR → T₂ → T₃
- w₂(B) → w₃(B): WW → T₂ → T₃ (redundant)
- r₂(B) → w₃(B): RW → T₂ → T₃ (redundant)
Precedence Graph:
T₁ → T₂ → T₃
Cycle check: No cycles (linear chain)
Topological sort: T₁, T₂, T₃
✓ S₁ is conflict-serializable
Equivalent to serial schedule: T₁ T₂ T₃The schedule naturally follows a serial order pattern. All conflicts point in the same direction, creating a simple chain T₁ → T₂ → T₃.
S₂: r₁(A) r₂(A) r₃(B) w₁(A) r₂(B) w₃(B) w₂(A) w₂(B)
Transactions: T₁, T₂, T₃
Data items: A, BPositions:
1: r₁(A) 2: r₂(A) 3: r₃(B) 4: w₁(A)
5: r₂(B) 6: w₃(B) 7: w₂(A) 8: w₂(B)
Conflict Analysis:
Data item A: r₁(A)@1, r₂(A)@2, w₁(A)@4, w₂(A)@7
- r₁(A)@1 vs w₂(A)@7: RW → T₁ → T₂
- r₂(A)@2 vs w₁(A)@4: RW → T₂ → T₁ ⚠️
- w₁(A)@4 vs w₂(A)@7: WW → T₁ → T₂
Data item B: r₃(B)@3, r₂(B)@5, w₃(B)@6, w₂(B)@8
- r₃(B)@3 vs w₂(B)@8: RW → T₃ → T₂
- r₂(B)@5 vs w₃(B)@6: RW → T₂ → T₃ ⚠️
- w₃(B)@6 vs w₂(B)@8: WW → T₃ → T₂
Precedence Graph Edges:
- T₁ → T₂ (from A)
- T₂ → T₁ (from A) ⚠️ 2-cycle!
- T₃ → T₂ (from B)
- T₂ → T₃ (from B) ⚠️ 2-cycle!
Cycles found:
- T₁ ↔ T₂ (bidirectional)
- T₂ ↔ T₃ (bidirectional)
✗ S₂ is NOT conflict-serializableMultiple 2-cycles exist. T₁ and T₂ have conflicting requirements on data item A. T₂ and T₃ have conflicting requirements on data item B. No serial order can satisfy all constraints.
S₃: r₄(D) r₁(A) w₄(D) r₂(B) w₁(A) r₃(C) w₂(B) w₃(C) r₄(A)
Transactions: T₁, T₂, T₃, T₄
Data items: A, B, C, DConflict Analysis:
Data item A: r₁(A)@2, w₁(A)@5, r₄(A)@9
- w₁(A)@5 vs r₄(A)@9: WR → T₁ → T₄
Data item B: r₂(B)@4, w₂(B)@7
- Only T₂, no conflicts with others
Data item C: r₃(C)@6, w₃(C)@8
- Only T₃, no conflicts with others
Data item D: r₄(D)@1, w₄(D)@3
- Only T₄, no conflicts with others
Precedence Graph:
- Only edge: T₁ → T₄
- T₂ and T₃ are completely independent
Graph visualization:
T₂
T₁ → T₄
T₃
Cycle check: No cycles!
Topological sorts (all valid):
- T₁, T₂, T₃, T₄
- T₂, T₁, T₃, T₄
- T₃, T₂, T₁, T₄
- T₂, T₃, T₁, T₄
- ... (many more, T₂ and T₃ can go anywhere, T₁ before T₄)
✓ S₃ is conflict-serializable
Constraint: T₁ must precede T₄. T₂, T₃ are independent.When transactions access disjoint data sets, they don't create edges in the precedence graph. Only T₁ and T₄ share data item A, creating the single constraint.
Experienced analysts recognize patterns that immediately indicate serializability or non-serializability.
Pattern 1: The Read-Write Swap
... rᵢ(X) ... wⱼ(X) ... rⱼ(Y) ... wᵢ(Y) ...
Creates: Tᵢ → Tⱼ (from X) and Tⱼ → Tᵢ (from Y)
2-cycle guaranteed!
Pattern 2: The Write-Write Swap
... wᵢ(X) ... wⱼ(X) ... wⱼ(Y) ... wᵢ(Y) ...
Creates: Tᵢ → Tⱼ (from X) and Tⱼ → Tᵢ (from Y)
2-cycle guaranteed!
Pattern 3: Bidirectional Access
Tᵢ reads X before Tⱼ writes X
Tⱼ reads Y before Tᵢ writes Y
Almost always creates a cycle. Check carefully!
Pattern 1: Monotonic Access
If all conflicts for all data items favor the same direction:
All edges: T₁ → T₂, T₁ → T₃, T₂ → T₃ (all point 'forward')
No back edges → acyclic → serializable
Pattern 2: Serial-Like Structure
If the schedule looks like T₁ operations, then T₂ operations, etc. (with minor interleaving of non-conflicting operations), likely serializable.
Pattern 3: Disjoint Access
Transactions accessing completely different data create no conflicts and add no edges:
T₁: operates on A, B
T₂: operates on C, D
No shared data → no edges → any serial order works
In exams: (1) First check for obvious 2-cycles—pairs of transactions with conflicts in both directions, (2) If no 2-cycles, build the full graph and look for longer cycles, (3) Draw the graph—visual inspection catches cycles faster than mental tracking. For 2-3 transactions, cycles are usually 2-cycles between a single pair.
| Observation | Likely Status | Verify By |
|---|---|---|
| Bidirectional edges exist | Non-serializable | 2-cycle immediately visible |
| All edges point one direction | Serializable | Confirm no back paths exist |
| Transactions share no data | Serializable | No edges = any order valid |
| Complex interleaving | Must analyze | Full graph + DFS for cycles |
The precedence graph is the culmination of conflict serializability theory—a practical, efficient algorithm that transforms theoretical concepts into actionable tests. Let's consolidate everything we've learned:
Module Complete:
You have now mastered Conflict Serializability—from the foundational concept of schedules, through serial schedules as the correctness benchmark, the various notions of equivalence, the details of conflict analysis, and finally to the precedence graph algorithm.
This knowledge forms the theoretical foundation for understanding:
You now have the tools to analyze any schedule and determine definitively whether it is conflict-serializable.
Congratulations! You have completed the Conflict Serializability module. You can now analyze transaction schedules, construct precedence graphs, test for conflict serializability, and find equivalent serial orders. This is fundamental knowledge for anyone working with database systems, from application developers to database administrators to system architects.