Two-Phase Locking
Concept. Two-phase locking acquires locks before releasing any of them. Follow that one rule and locking yields a conflict-serializable schedule, with no need to test it. Strict 2PL, the version real databases ship, holds every lock until commit.
Intuition. Locking one row at a time is not enough. When Mickey reads a balance, releases the lock, then writes it back, Minnie can slip into the gap and the result is garbage. 2PL closes that gap: Mickey holds every lock he takes until he is done acquiring, so no one can interleave between his read and his write. The transaction looks atomic to everyone else.
A lock protects one row, in two modes: shared for reads, exclusive for writes. But a transaction holds many locks across its life. Release one too early and another transaction slips into the gap, reading work that may still be undone. The rule that prevents it is Two-Phase Locking.
Two-Phase Locking (2PL): Growing then Shrinking
Locks are the primitive. The protocol for when to acquire and release them is Two-Phase Locking.
Two-Phase Locking (2PL) gives every transaction two phases: a growing phase that only acquires locks, then a shrinking phase that only releases them. The first unlock flips growing to shrinking, and the transaction can never acquire again.
Figure: 2PL: locks held rise during the growing phase, peak, then fall during the shrinking phaseA lock-count curve over the life of one transaction. Locks held (violet) climb step by step through the growing phase, peak, then fall step by step through the shrinking phase. A dashed divider marks the first unlock, the moment the protocol crosses from growing to shrinking. Color key: violet is the locks-held curve.
Figure 1. 2PL: the lock count only rises, then only falls. The peak is the moment the transaction holds everything it will ever need.
Strict 2PL (S2PL) is the version real databases ship: hold every lock until commit or abort, then release them all at once.
Figure: Strict 2PL (S2PL): locks held rise during the growing phase, plateau, then drop to zero in one step at COMMITA lock-count curve over the life of one transaction under Strict 2PL. Locks held (violet) climb step by step, plateau while the transaction holds every lock, then drop to zero in a single step at commit. A dashed divider marks the commit, the only point where locks release. Color key: violet is the locks-held curve.
Figure 2. Strict 2PL: locks rise, plateau, then drop to zero in one step at commit. Nothing is released early.
The two-phase rule makes locking correct. As long as every transaction grows then shrinks, the resulting schedule is provably equivalent to some serial run. You don't have to verify schedules; you verify that everyone played by the rule. The proof that this yields a conflict-serializable schedule lives on Correctness; 2PL is the protocol that earns that guarantee.
The Cost of Releasing Early: Cascading Aborts
Why does Strict 2PL wait until commit to release anything? Because releasing early opens a failure that regular 2PL cannot stop.
Figure: Why strict 2PL stops cascading abortsTwo protocols compared. Under regular 2PL, T1 writes x then unlocks it early, T2 reads that uncommitted value, and when T1 aborts, T2 has read a value that never committed and must abort too, a cascading abort. Under strict 2PL, T1 holds the lock on x until it commits or aborts, so T2 waits and only ever reads a committed value, and there is no cascade.
Figure 3. Release a lock early (regular 2PL) and another transaction can read work you later undo, so its abort cascades into theirs. Strict 2PL holds every lock to commit, so no one ever builds on uncommitted work. It is the sous-chef who hands over a sauce only once the head chef has approved it.
What 2PL buys, and what it costs. It prevents lost updates, and its strict form prevents dirty reads too. Strict 2PL ships in Postgres, MySQL, SQL Server, and Oracle, and still lets non-conflicting transactions run in parallel. The costs: a transaction blocks on any conflicting lock, holding locks until commit is conservative, and two transactions can end up waiting on each other in a cycle.
That last cost is the one new risk locks create. Two transactions, each holding a lock the other needs, wait forever. Microschedules & Deadlock is where that risk gets detected and broken.