Databases Reference/Storage Internals

External Sort & Large Queries

What happens when a sort or ORDER BY doesn't fit in memory — the two-phase chunk-sort-then-k-way-merge behind Postgres's work_mem spill-to-disk and MapReduce's shuffle phase.

3/5Overview: 25m

The problem, precisely

n items on disk, too large to fit in memory of size M (M ≪ n), need to end up fully sorted, also on disk. Disk I/O is orders of magnitude slower than RAM, so the metric that matters is number of passes over the data, not comparison count.

External merge sort — two phases

PhaseWhat happens
1. Sort in chunksRead M-sized chunks, sort each in memory, write each sorted chunk back to disk — ⌈n/M⌉ sorted files
2. K-way mergeMin-heap holding the current smallest unconsumed value from each chunk file; pop, write to output, refill from that chunk — never load more than one block per chunk at a time
Write → memtable-sized chunks → sort each in RAM → k sorted files Merge → min-heap of k file pointers → pop smallest → write → refill

Phase 2 is the identical shape to merging k sorted linked lists, just with file handles instead of pointers. If the number of chunks exceeds how many file handles/heap entries you can hold open at once, merge in multiple rounds (B chunks per round) until one file remains — the pass count, roughly log_B(n/M), is the number engineers actually tune.

Where this shows up

Postgres ORDER BY. The work_mem setting caps how much memory one sort may use. Under the cap, Postgres sorts in memory; over it, it transparently falls back to external merge sort — visible directly via EXPLAIN (ANALYZE, BUFFERS): Sort Method: external merge Disk: NNNkB instead of Sort Method: quicksort Memory: NNNkB. A query that's mysteriously slow on a large ORDER BY is a strong candidate for "raise work_mem so the sort stays in memory" — a real, checkable operational lever, not just interview trivia.

MapReduce's shuffle phase. Each mapper sorts its own local output (phase 1); each reducer merges the sorted outputs it receives from every mapper before its reduce function runs (phase 2) — the same two-phase structure, with "chunk" replaced by "one mapper's partition" and the merge happening over the network instead of local disk. "Group by key at a scale no single machine holds" is fundamentally an external-sort problem.

Cost model

CostWhy it dominates
Disk passes (O(n/B) block transfers per pass)Each full read+write pass over the data, not comparisons, is what wall-clock time actually tracks
Merge fan-in limitA real system can't hold unlimited file handles/heap entries open — bounds how many chunks merge per round, which sets the pass count

Not covered here

The in-memory sort used for each chunk — see the DSA roadmap's Sorting Algorithms topic. The min-heap merge itself is the same K-Way Merge pattern used for merging k sorted lists, with full complexity analysis and practice problems there.

Where this goes next

ACID & Isolation Levels moves from how bytes persist to what concurrent transactions are allowed to see.

Further Reading

Hands-On Tasks (Optional)

Low-setup exercises — schema drills, paper walkthroughs, or optional local installs. No autograding; the goal is interview fluency on how data is stored.

  • Diagnose a slow ORDER BY

    A report query's EXPLAIN ANALYZE shows `Sort Method: external merge Disk: 850MB`. Explain in three sentences why this happens, what work_mem controls, and the one-line operational fix — plus the trade-off of just raising work_mem globally on a busy server.

    15m