A table is stored clustered on its primary key. Compare what happens on insert when that key is a monotonically increasing value versus a random one such as a version-4 UUID.
answer
- Clustered = row must land in key position
- Monotonic → rightmost page hot, near-100% fill
- Random → split everywhere, ~70% fill, bloat
- Random → read-before-write once index > memory
- Fix: time-ordered id or surrogate key; monotonic costs right-edge contention
basics
~20 sAn increasing key always inserts at the rightmost leaf: pages fill in order, few splits, compact table, small write set. A random key inserts everywhere, so many leaf pages are touched and split, pages end up half full, the table bloats and writes scatter across memory and storage.
solid answer
~50 sClustered storage forces every row into its key-ordered position, so insert *order* is a physical property. **Monotonic key (sequence, ordered timestamp-based id):** every insert lands in the rightmost leaf. That page stays hot and cached; when it fills, the split is a cheap right-edge split leaving the left page nearly full. Result: high page fill, minimal fragmentation, a small dirty-page working set, and compact write-ahead-log volume. **Random key (UUIDv4):** each insert targets an unpredictable leaf. Consequences: (a) the target page may not be cached, so the insert first reads it; (b) splits happen all over the tree and typically leave both halves about half full, so the table can occupy roughly twice the pages; (c) the dirty page set is large, so checkpoints flush far more pages; (d) worse cache hit rate as the whole index becomes the working set. Mitigations: use a sequence or a time-ordered identifier (UUIDv7-style), or keep the random value as a non-clustering unique column with a narrow surrogate as the clustering key.
code
text · 7 linesMONOTONIC KEY
insert -> leaf[last] leaf[last] leaf[last] ... one hot cached page
split -> right-edge, left page stays ~full
RANDOM KEY (uuid v4)
insert -> leaf[8321] leaf[112] leaf[59004] ... random reads first
split -> mid-page 50/50, steady-state fill ~65-70%go deeper
Know that increasing keys append at the end while random keys insert all over, causing more splits and a larger table.
Explain the mechanism and the four consequences — read-before-write, poor fill, large dirty set, whole-index working set — and name a time-ordered id as the fix.
Quantify: steady-state fill under random inserts, footprint and cache impact, checkpoint write amplification, and the right-edge contention counter-tradeoff.
Turn it into a key-strategy decision across services — client-generated versus server-generated identity, uniqueness and unpredictability requirements, and the cost curve as the table outgrows memory.
## Why insert order is physical under clustering In a clustered table, rows are stored in the leaf pages of a B+tree ordered by the clustering key. There is no "append anywhere" option: a new row must be placed in the leaf that covers its key range. So the distribution of incoming key values directly determines which pages the engine touches, dirties and splits. In a heap this concern largely disappears — new rows go to any page with space, usually at the end — which is exactly why this question is specific to the clustered model. ## Monotonically increasing keys With a sequence, identity column, or otherwise increasing key, every insert belongs at the right edge of the key space. - **One hot page.** All inserts hit the current rightmost leaf, which stays in memory. No read is needed before writing. - **Cheap splits.** When that page fills, engines commonly perform a right-edge optimization: rather than splitting 50/50, they leave the full page as-is and start a new page. Fill factor stays near 100%. - **Compact table.** High fill means fewer pages for the same rows: smaller table, better scan throughput, more rows per cached page. - **Small dirty set.** Only a handful of pages are dirty at any moment, so checkpoints and background flushing move little data, and write-ahead-log volume per insert is minimal. - **Good physical/logical correlation.** Rows inserted together are stored together, so "recent rows" queries have excellent locality. ## Random keys such as UUIDv4 A v4 UUID is essentially uniform random over a 128-bit space, so consecutive inserts land in unrelated parts of the tree. - **Read-before-write.** The target leaf is unlikely to be cached once the index exceeds memory, so an insert becomes a random read followed by a modification. Insert throughput becomes bound by random read latency. - **Splits everywhere, poor fill.** Random insertion into a full page causes a mid-page split leaving two pages roughly half full. Steady state for uniformly random inserts is around 65-70% average fill, so the table occupies noticeably more pages than the same data inserted in order — a meaningful footprint increase, and correspondingly less effective caching. - **Large dirty set.** Because writes scatter, many distinct pages are dirty simultaneously; checkpoints must flush all of them, converting what could have been a few sequential writes into many random ones. - **Whole index becomes the working set.** With ordered inserts, the hot part of the index is the right edge; with random inserts, the hot part is everything, so cache hit rate collapses once the index exceeds memory. This is the cliff people describe as "it was fine until the table hit N rows". - **Width cost on top.** A 16-byte UUID (or worse, a 36-character text representation) is wider than an 8-byte integer, lowering fanout and — under clustered storage — inflating every secondary index that carries it as the row reference. That is a separate penalty compounding the ordering one. ## The counter-consideration: hot-page contention Monotonic keys are not free. Because every insert targets the same rightmost leaf, that page becomes a contention point: concurrent inserters serialize on its latch. At high concurrency this shows up as latch contention on the right edge. This is a real tradeoff, not a reason to prefer random keys — mitigations are more targeted, and the random-key remedy trades a contention problem for a much larger I/O and footprint problem. ## Practical remedies - **Time-ordered identifiers.** UUIDv7-style values put a timestamp in the high-order bits, so they sort roughly by creation time while retaining global uniqueness and non-guessability in the low bits. This recovers nearly all the insert locality of a sequence. - **Narrow surrogate clustering key.** Cluster on a compact sequence and keep the random identifier as an ordinary unique column with its own secondary index. Inserts stay ordered; external lookups by UUID cost one extra descent. - **Reordering an existing UUID.** Where the identifier's byte layout puts a timestamp in a non-leading position, storing a byte-rearranged form so the time component leads restores ordering — an option only if you control the representation. - **Fill factor tuning.** Leaving free space in pages reduces split frequency for random inserts, at the price of a larger table up front. This softens the symptom rather than removing the cause. ## How to answer Lead with the mechanism — clustered storage forces the row into key position — then name the four consequences of randomness: read-before-write, splits with poor fill, a large dirty set, and the whole index becoming the working set. Then give the mitigation ladder, and be honest that monotonic keys concentrate contention at the right edge.
- Is a monotonically increasing clustering key always the right choice?Not always. Because every insert targets the same rightmost leaf, that page becomes a concurrency hot spot and concurrent inserters contend on it, which can cap insert throughput on high-core systems. It also correlates physical position with insertion time, which is usually helpful but occasionally undesirable. Even so, the contention problem is narrower and more tractable than the cache-miss and bloat problem that random keys create.
- How does a time-ordered UUID variant such as UUIDv7 help?It places a timestamp in the high-order bits, so generated values sort approximately in creation order while the remaining bits keep them unique and unpredictable. Inserts therefore cluster at the right edge of the tree as a sequence would, restoring high page fill and a small dirty set. You keep client-side generation and global uniqueness without paying the random-insert penalty, though the value is still wider than an integer.
- Why does a random clustering key make the table occupy more pages for the same data?Random inserts into full pages cause mid-page splits that leave each half roughly half full, and subsequent inserts refill them only gradually and unevenly. The steady-state average fill for uniformly random insertion settles well below full — commonly cited around 65-70% — so the same rows need more pages than an ordered load would. More pages means more bytes to cache, scan and back up.
Filing into a full alphabetical cabinet: adding names in order means always working at the last drawer; adding random names means opening a different drawer every time and shoving half of it into a new one.
saying these in an interview costs you the question
- Claiming insert order doesn't matter because the B+tree is balanced anyway
- Believing random UUID keys only cost extra bytes, not extra I/O
- Saying page splits are equally frequent regardless of key order
- Assuming a monotonic key is free of downsides, ignoring right-edge latch contention
- Proposing to fix random-key bloat purely by rebuilding the index, without changing the key