skip to content

Some database engines take intent locks — modes named IS, IX and SIX — on a table before locking individual rows inside it. What problem do intent locks solve, and how does SIX differ from IX?

level: seniorimportance: should knowfreq 38%

answer

  1. Intent = announcement of finer locks below
  2. IX ∥ IX compatible — otherwise row locking pointless
  3. Table-S conflicts with IX
  4. SIX = read whole object + write some rows
  5. Acquire top-down, release bottom-up

basics

~20 s

Intent locks announce at the table level that finer locks exist below, so a transaction wanting the whole table can detect a conflict in one check instead of scanning every row lock. IS means shared locks below, IX exclusive locks below, SIX means shared on the whole table plus exclusive locks on some rows.

solid answer

~60 s

Without intent locks, a transaction that wants an exclusive lock on a whole table would have to inspect every row lock in the manager to know whether anyone is working inside it — O(rows) per request. **Multiple-granularity locking** fixes this: before locking a row in mode S or X, a transaction first takes the corresponding **intent** mode on the ancestors — **IS** before an S row lock, **IX** before an X row lock. The table entry now advertises what lives beneath it, so a table-level request is a single compatibility check. Intent modes are compatible with each other — IS and IX coexist freely, because two transactions locking *different* rows must not block. Conflicts appear only against the real table-level modes: table-S conflicts with IX, table-X conflicts with everything. **SIX** is the composite: shared on the entire table *and* intent-exclusive beneath it. It is what a transaction takes when it reads the whole table but will modify a few rows — a scan-and-update. SIX blocks other writers (they need IX or X) while still admitting readers holding IS.

code

text · 6 lines
text
IS    IX    S     SIX   X
  IS      Y     Y     Y     Y     N
  IX      Y     Y     N     N     N
  S       Y     N     Y     N     N
  SIX     Y     N     N     N     N
  X       N     N     N     N     N

go deeper

for a junior

Knowing intent locks exist and that they let the engine check table-level conflicts without scanning every row lock is enough at this level.

for a middle

State the acquisition rule (intent on ancestors before the real lock on the row) and that intent modes are compatible with one another.

for a senior

Reproduce the matrix, explain SIX as read-all/write-some, and note that lock conversion between intent modes queues and can deadlock.

for a principal

Discuss it as the mechanism that makes mixed-granularity locking viable at all, and how a never-escalating engine trades lock-manager memory for concurrency while intent modes keep coarse operations like DDL cheap to arbitrate.

## The problem intent locks exist to solve Suppose transaction A holds an exclusive lock on one row deep inside a 50-million-row table. Transaction B now wants to lock the whole table exclusively — say, to run `ALTER TABLE`. How does B find out it must wait? With only S and X modes on rows, B would have to walk the entire lock manager looking for any lock whose resource belongs to that table. That is a linear scan on a hot, latched data structure, executed on every coarse lock request. It does not scale. **Multiple-granularity locking (MGL)** solves it with a rule: *before you may lock a node in the hierarchy, you must first lock all of its ancestors in a compatible intent mode.* The table entry then carries a summary of everything happening below it, and a table-level request becomes a single O(1) compatibility check. ## The intent modes - **IS — intent shared.** "I hold, or am about to hold, shared locks on some descendants." Taken on the table before an S row lock. - **IX — intent exclusive.** "I hold, or am about to hold, exclusive locks on some descendants." Taken on the table before an X row lock. - **SIX — shared with intent exclusive.** The composite: a real S lock on the whole object *plus* IX beneath it. Semantically "I am reading everything and writing some of it." Some engines also define IX-variants such as UIX or a separate schema-stability mode; those are refinements, not different ideas. ## The compatibility matrix ``` IS IX S SIX X IS yes yes yes yes no IX yes yes no no no S yes no yes no no SIX yes no no no no X no no no no no ``` Read it as "held mode (row) against requested mode (column)". Two facts carry the whole design: 1. **Intent modes are mutually compatible.** IS/IX/IX all coexist on the same table. They must — otherwise two transactions updating different rows of the same table would serialise, destroying the entire point of row-level locking. The intent lock is an *announcement*, not a reservation; the actual conflict is resolved at the row level. 2. **Intent conflicts with the real coarse modes.** A table-level S is incompatible with IX, because someone is writing rows underneath and a whole-table reader must not see them change. A table-level X is incompatible with everything, because it claims the entire subtree. ## Where SIX earns its place Consider a transaction that scans an entire table and updates a handful of rows it finds: ```sql UPDATE accounts SET fee = fee * 1.05 WHERE status = 'delinquent'; ``` Without SIX the transaction would need either a table-level X — far too strong, blocking innocent readers — or S plus IX, which the matrix forbids as a pair on the same object. SIX packages exactly the right combination: the whole table is read-stable for this transaction, other readers holding IS may proceed, and other *writers* (who need IX, S, SIX or X) are all blocked. That asymmetry is the interview point. SIX is strictly stronger than IX and strictly weaker than X: it admits readers, excludes writers. ## Lock conversion and the hierarchy Modes are not fixed for a transaction's lifetime. A transaction that starts with IS on a table and then decides to write must **convert** its table lock to IX (or SIX if it already holds table-level S). Conversion requests queue like any other and can participate in deadlocks — two transactions each holding IS and each requesting SIX will wait on each other until the detector abandons one. The hierarchy also extends downward. In an engine with page locks the chain is table → page → row, and a row X lock requires IX on the page *and* IX on the table. Acquisition goes top-down; release goes bottom-up, which is what keeps the summary at each level truthful at all times. ## Which engines expose this MGL with explicit IS/IX/SIX is the classic System R design and is visible in SQL Server and DB2. MySQL/InnoDB implements intent locks too: `IS` and `IX` are genuine table-level modes there, and `SELECT ... FOR UPDATE` takes IX on the table before X on the rows. PostgreSQL takes the same idea with different naming — `ROW SHARE` and `ROW EXCLUSIVE` are its intent modes, and they are precisely what makes `ACCESS EXCLUSIVE` (taken by DDL) a one-check conflict test. ## What interviewers listen for The efficiency argument first: intent locks turn an O(rows) conflict search into O(1). Then the counter-intuitive compatibility fact — IX is compatible with IX, because the announcement is not the conflict. And finally SIX as the read-all/write-some composite, with the correct claim that it blocks writers while still admitting IS readers.

  • Why are IX and IX compatible with each other when X and X are not?
    Because an intent lock only advertises that finer locks exist below; it does not claim any specific row. Two transactions updating different rows of the same table both need IX on the table, and blocking them there would defeat row-level locking entirely. The real conflict, if both target the same row, is caught at the row level where both request X.
  • Which intent lock does a plain indexed `UPDATE` of one row acquire on the table, and why not a stronger one?
    Intent exclusive (IX). The transaction will hold an exclusive lock on one row, so it must announce that at the table level, but it makes no claim on the rest of the table. Taking table-level S or X instead would needlessly block transactions working on unrelated rows.
  • How does an engine detect that a DDL statement must wait for in-flight row-level work?
    The DDL requests the strongest table mode — ACCESS EXCLUSIVE in PostgreSQL, X in SQL Server — and that mode is incompatible with every intent mode. One compatibility check against the table's lock entry reveals any IS or IX held by an active transaction, so the DDL queues without inspecting a single row lock.

A hotel front desk board. Guests holding individual room keys hang a tag at reception saying "someone is in a room" (intent). The cleaning crew wanting the whole floor only has to glance at the board rather than knock on every door — and two guests in different rooms never conflict, because the tag announces, it does not reserve.

saying these in an interview costs you the question

  • Saying IX blocks other IX holders — that would serialise all writers to a table and make row locks pointless
  • Describing an intent lock as reserving specific rows; it only announces that some descendant is locked
  • Treating SIX as merely a synonym for X — SIX still admits IS readers
  • Claiming intent locks are needed for correctness; they are a performance mechanism for conflict detection, and correctness comes from the row-level modes
  • Assuming PostgreSQL has no intent locks because it lacks the IS/IX names — ROW SHARE and ROW EXCLUSIVE are exactly that

context