4.4.19 · D4Databases

Exercises — Locking — shared, exclusive, intent locks

2,234 words10 min readBack to topic

Everything here rests on one object — the compatibility matrix — so let us pin it down before symbols fly.

The figure below is the picture we will point at again and again.

Figure — Locking — shared, exclusive, intent locks

Level 1 — Recognition

Goal: read the matrix and the vocabulary without hesitation.

Recall Solution 1.1

Granted. Two readers see the same unchanged value — S–S is the only compatible pair among the base modes. Why? Reading changes nothing, so parallel reads are safe.

Recall Solution 1.2
  • (a) → S (Shared)
  • (b) → X (Exclusive)
  • (c) → IX (Intent Exclusive — announces an X below)
  • (d) → SIX (S on the node + IX for children)
Recall Solution 1.3

True. The entire X row and X column are ❌. A writer needs total isolation, so no S, no X, no intent lock may coexist with it on the same item.


Level 2 — Application

Goal: run the locking protocol on a real statement.

Recall Solution 2.1
  1. IX on table accounts — "I intend to write a row inside."
  2. X on row 5 — the actual write lock.

Why the order? You must announce intent on every ancestor before locking the child; otherwise a coarse locker cannot detect you.

Recall Solution 2.2
  1. IS on table accounts.
  2. S on row 5.

Read needs the intent-shared announcement, then the shared row lock.

Recall Solution 2.3

T2 waits. Decision cell: existing IX vs requested S → ❌. T2 never scans rows; the table-level IX flag alone tells it the table is "written somewhere below." That is the whole point of intent locks: an lookup instead of an scan.


Level 3 — Analysis

Goal: reason about why cells conflict, and about interleavings.

Recall Solution 3.1

Yes, concurrent.

  • Table level: T1 gets IX, T2 gets IX → IX–IX is ✅ (both merely announce writes below).
  • Row level: T1 locks row 5 with X, T2 locks row 99 with Xdifferent items, so the X–X rule never triggers (that rule is per-item).

The X–X conflict only fires when two writers hit the same row.

Recall Solution 3.2
  • IX–S ❌: a full-table reader (S) can't coexist with someone writing some row (IX) — the write could alter a row the reader sees.
  • IX–IX ✅: both only announce writes below; disjoint rows never collide.
  • SIX–IX ❌: SIX already holds S on the whole node; a new writer's intent (IX) conflicts with that node-wide read.
  • X–IS ❌: the whole item is exclusively locked; no intent to even read a child is allowed.
Recall Solution 3.3

Anomaly: lost update. T2's write overwrites T1's, so one increment vanishes. Prevention: T1 should take an X lock on before writing (indeed, hold it from its first read under Two-Phase Locking). Then T2's read (S) at t2 would wait — S–X conflict — until T1 commits, forcing .


Level 4 — Synthesis

Goal: combine intent + base locks into a full multi-level plan and reason about starvation.

Recall Solution 4.1

This is the classic SIX pattern — read everything, write a few.

  1. SIX on table accounts = S over the whole table (for the scan) + IX (intent to write children).
  2. X on row 5, X on row 99 (the two updates).

One coarse lock (SIX) simultaneously guards the full read and announces the pending writes. See Lock Granularity.

Recall Solution 4.2
  • T2 wants S: existing SIX vs requested S → ❌. Waits. (SIX's IX component conflicts with a table-wide reader.)
  • T2 wants IS: existing SIX vs requested IS → ✅. Granted — IS is compatible with everything except X. T2 may then read individual rows that aren't X-locked.
Recall Solution 4.3

Writer starvation. Because S–S is compatible, readers keep overlapping so the S lock on row 5 is never fully released; the X request (which needs zero S locks) waits forever. Fixes: a lock queue with fairness (once X is queued, block new S requests behind it), or switch to MVCC where readers don't take row locks at all — see Isolation Levels.


Level 5 — Mastery

Goal: integrate locking with deadlock, 2PL, and higher-level guarantees.

Recall Solution 5.1

Deadlock. T1 waits on T2's row 99; T2 waits on T1's row 5 — a cycle. See Deadlocks. IX–IX being ✅ only settled the table level; the fatal conflict is X–X at the row level, on the two specific rows they cross-request. The engine breaks it by aborting one transaction (a victim).

Recall Solution 5.2

A writer holds X on every item it touches until commit. Any reader needs S on that item, but S–X is ❌, so the reader cannot observe the item until the writer commits (or aborts, undoing the change). Therefore no transaction ever reads uncommitted data → no dirty reads.

Recall Solution 5.3
  • Without intent locks: the table-lock request must check each of the rows for an existing X → scan.
  • With intent locks: the writer left an IX flag on the table; the S request checks that single table-level entry → .

Intent locks convert an scan into an matrix lookup — the entire reason the mechanism exists.


Cell IX–S is ❌
so a table SHARE lock waits behind an updater's table-level IX
IX–IX compatible but rows still deadlock
because the conflict is X–X at the row level, not at the table
Scan all then write a few
the SIX pattern (S on node + IX for children)
Intent locks change conflict detection cost
from row scan to table-flag lookup
Does 2PL prevent deadlocks
no — it ensures serializability only; deadlocks need separate handling