Relational engines take two broad approaches to storing superseded row versions: writing the old image into a separate undo or rollback area, or keeping every version inside the table's own data pages. Compare how each reclaims space and what operational problems each creates.
answer
- undo = in-place row + before-image elsewhere
- heap = append new tuple, mark old dead
- undo: readers walk chains, space can exhaust
- heap: bloat + index entry per version
- rollback cheap in heap, costly in undo
basics
~20 sUndo-based engines update rows in place and keep before-images in a separate area that a purge process trims; the table stays compact but undo can explode and old reads get slower. In-page engines append new versions into the table and rely on a vacuum-style process; current reads stay fast but tables and indexes bloat.
solid answer
~50 s**Undo/rollback style** (InnoDB, Oracle): the row is modified in place and the previous image is written to a separate undo area. A purge thread discards undo once no snapshot needs it. Upside: the table itself stays compact, and index entries for unchanged keys stay stable. Downside: a reader of an old snapshot must reconstruct the version by walking an undo chain, so long readers get progressively slower; undo space grows with the oldest open transaction and can exhaust its tablespace; rollback of a huge transaction is expensive. **In-page / heap versioning** (PostgreSQL style): a new tuple is appended into the table's pages and the old one is marked dead. Upside: current reads never chase chains, and rollback is nearly free. Downside: tables and indexes bloat, every version usually needs new index entries so writes amplify, and you depend on a vacuum process keeping up. Both defer reclamation behind the same horizon rule; they differ only in *where* the garbage sits and therefore in how it hurts.
go deeper
Know that some engines keep old images in a separate undo area and others keep versions in the table itself, and that both need a cleanup process.
Contrast the reclamation paths and name the direction each fails: undo exhaustion and slow old reads versus table and index bloat with write amplification.
Map each design to the alarm you would set and the workload advice that follows, and note that both are still bounded by the oldest open transaction.
Discuss it as a storage-engine selection trade under a known workload profile: update-heavy narrow rows, long analytical reads, rollback frequency, rather than as a ranking.
## Same problem, two places to put the garbage Every MVCC engine must keep superseded row images until no snapshot needs them, and must then reclaim the space. The families differ in where those images live, and that choice propagates into performance, failure modes and operational tooling. ## Family 1: out-of-place versions in an undo area The current row lives in the table page and is modified **in place**. Before modifying it, the engine copies the prior image into a dedicated undo/rollback structure and links the row to it. A reader whose snapshot predates the change follows the link and applies undo records backwards until it reconstructs the version it is entitled to see. **Reclamation** is done by a purge (or undo-truncation) process that discards undo records older than the oldest snapshot. Because undo is a separate, roughly append-and-trim structure, reclaiming it is comparatively cheap and does not require touching table pages. **Strengths.** The table stays close to live size, so scans and cache efficiency degrade little under churn. Secondary index entries for columns that did not change need not be rewritten. Clustered or index-organised storage stays physically ordered. **Weaknesses.** - *Read amplification for old snapshots.* A long report reading a hot table walks longer and longer undo chains; its cost grows with how much has changed since it started. A query can slow down by an order of magnitude purely because writers are busy. - *Undo growth.* Undo cannot be purged past the oldest snapshot, so one long transaction inflates the undo area rather than the table. Undo space is often bounded, so exhaustion is an outage: writes start failing, or long readers are aborted. - *Bounded retention errors.* When the needed undo has been discarded or overwritten, the reader cannot be served and is aborted, the classic "snapshot too old" style failure. - *Expensive rollback.* Undoing a huge transaction means applying every undo record; a rollback can take as long as the work it reverses. ## Family 2: append-in-table versions in the relation's own pages An UPDATE writes a **new tuple** into the table (ideally the same page) and marks the old one dead as of the commit. Nothing is reconstructed: readers scan versions and test visibility directly. **Reclamation** is a vacuum-style pass over the relation, plus opportunistic in-page pruning. It must visit table pages *and* remove the corresponding index entries, so its cost scales with the size of the table and its indexes, not with the volume of garbage alone. **Strengths.** Reading current data is direct, with no chain walking, so a long report does not get slower as writers churn. Rollback is essentially free: the aborted transaction's versions are simply never visible, and cleanup collects them later. Concurrent readers of hot rows do not contend on a shared undo structure. **Weaknesses.** - *Table and index bloat*, with all the scan, cache and backup costs that implies. - *Write amplification.* A new version generally means a new entry in every index, unless an optimisation lets an update that touches no indexed column keep versions inside the same page and off the indexes. This is why "do not update indexed columns needlessly" is real advice in these engines. - *Dependence on a maintenance process.* If vacuum-style cleanup cannot keep up or is blocked by the horizon, degradation compounds: more bloat means slower cleanup means more bloat. - *Transaction-id housekeeping.* Visibility stamps are finite, so these engines also need a periodic freezing pass over old data; neglecting it can force emergency maintenance. ## The trade in one sentence each Undo-based engines convert version retention into **a separate space that can run out and reads that get slower**; heap-versioned engines convert it into **table and index space that grows and maintenance that must keep up**. Neither escapes the horizon rule; both are hostage to the oldest open transaction. ## What this means for you as an engineer The portable advice is identical: keep transactions short, chunk batch jobs, run long analytics somewhere with its own retention, and monitor the age of the oldest snapshot. The engine-specific part is only which alarm you set: undo/rollback space consumed and long-read latency on one side, relation-versus-live-size ratio and cleanup lag on the other. In an interview, naming both families and the direction each one fails in is the answer; reciting one vendor's knobs is not.
- Why does an hour-long report tend to get slower over time on an undo-based engine but not on a heap-versioned one?On an undo-based engine the report reads the current row and then walks backwards through undo records to reconstruct the image its snapshot is entitled to see. The more the table changes while the report runs, the longer those chains get, so per-row cost rises. A heap-versioned engine stores each version directly in the table and tests visibility per version, so there is no chain to walk and the cost stays roughly flat, at the price of scanning extra dead versions.
- Why does 'avoid updating indexed columns' matter more in heap-versioned engines?A new heap version normally needs a matching entry in every index, which multiplies write cost and index bloat. These engines have an optimisation whereby an update that changes no indexed column can keep the new version in the same page and reuse the existing index entries. Updating an indexed column disqualifies that path, so the same workload suddenly writes to every index and bloats much faster.
saying these in an interview costs you the question
- Claiming undo-based engines avoid MVCC garbage entirely rather than relocating it
- Saying heap-versioned engines are simply worse, ignoring free rollback and chain-free current reads
- Believing one family is exempt from the oldest-snapshot horizon
- Assuming rollback cost is the same in both designs
- Thinking undo space is unbounded and can never cause an outage