Two-Phase Locking (2PL)

Two-Phase Locking is the standard concurrency control protocol in database systems.


How They Work

📈 Two-Phase Locking (2PL)

Rule: Two distinct phases

Phase Structure:
1. Growing Phase: Acquire locks, no releases
2. Shrinking Phase: Release locks, no acquisitions

Transition: First unlock marks the switch to shrinking phase

Two-Phase Locking diagram

🔒 Strict 2PL (S2PL)

Rule: Hold all locks until commit/abort

• No shrinking phase during execution
• All locks released atomically at transaction end

Key Benefit: Prevents cascading aborts

Strict Two-Phase Locking diagram

The Cascading Abort Problem 🚨

Imagine: Sous-chef1 makes dressing, Sous-chef2 takes it for salad, but head chef hasn't approved yet...

🥗 Kitchen Disaster: The Salad Dressing Cascade

😰 Regular 2PL - The Cascade

Strategy: Release ingredients as soon as done

Timeline:
1. Sous-chef1 starts making vinaigrette dressing
2. Sous-chef1 finishes and releases dressing to shared station
3. Sous-chef2 takes the dressing for Caesar salad
4. Sous-chef2 mixes dressing with lettuce and serves
5. Head chef tastes Sous-chef1's dressing: "Too acidic!"
6. Disaster: Sous-chef1 must redo dressing, BUT Sous-chef2's salad already served with bad dressing!

Result: Both dishes ruined - cascading failure

✅ Strict 2PL - No Cascade

Strategy: Keep everything until approved

Timeline:
1. Sous-chef1 starts making vinaigrette dressing
2. Sous-chef1 finishes but keeps dressing until approved
3. Sous-chef2 wants dressing but waits...
4. Head chef tastes: "Too acidic!"
5. Sous-chef1 fixes dressing privately, then releases
6. Success: Sous-chef2 gets perfect dressing for salad

Result: Only good dressing ever leaves the station

The Point: S2PL ensures others only see your "approved" (committed) work - like waiting for head chef approval before sharing ingredients.


Lock Sequences: 2PL vs S2PL 🔍

Regular 2PL - Cascading Abort

T1: BEGIN
T1: lock-X(x)
T1: w(x)             -- modifies data
T1: unlock(x)        -- EARLY RELEASE!

T2: BEGIN
T2: lock-S(x)        -- gets lock
T2: r(x)             -- dirty read!
T2: lock-X(y)     
T2: w(y)             -- uses dirty data
T2: unlock(x)
T2: unlock(y)

T1: ABORT            -- rollback

--> T2 used invalid data! 
    Must CASCADE ABORT

Strict 2PL - No Cascades

T1: BEGIN
T1: lock-X(x)
T1: w(x)             -- modifies data
                     -- HOLDS LOCK

T2: BEGIN
T2: lock-S(x)        -- WAITS... 
                     -- blocked...
                     -- blocked...

T1: ABORT            -- rollback
T1: unlock(x)        -- releases at abort

T2: r(x)             -- reads original value!
T2: lock-X(y)
T2: w(y)             -- uses clean data
T2: COMMIT
T2: unlock(x)
T2: unlock(y)

--> T2 gets original value!
    No cascade needed

Key Insight: Regular 2PL allows dirty reads via early lock release. S2PL holds locks until transaction end, preventing dirty reads and cascading aborts.


2PL and S2PL for Concurrency

✅ Benefits

  • Correctness: Prevents data corruption
  • Simplicity: Easy to implement and understand
  • Proven: Used by all major databases
  • Parallelism: Non-conflicting transactions run concurrently

⚠️ Costs

  • Blocking: Must wait for conflicting locks
  • Reduced Concurrency: Conservative lock holding
  • Lock Overhead: Managing lock tables and queues
  • Wait Cycles: Transactions may need to wait for each other