Storage: Physical Organization
What should sit next to what, given that I must decide at write time and find out at read time?
Who this mechanism is for: a developer who writes code, calls APIs and knows basic SQL, but has never studied how a database or a data lake lays its bytes out on disk. You do not need any database knowledge. Every technical term is explained the first time it appears, and again in the glossary at the end. If you know what a file and a table are, you can read this mechanism.
The story we will follow
Kavya is a data engineer at Parcelo, a parcel-delivery company. Every time a depot scans a parcel, one row is added to a table called scan_events. The table now has about 2 billion rows, 25 columns and roughly 200 bytes per row, so about 400 GB in total. It lives in cloud object storage (a service such as Amazon S3 that stores files and charges per request). Her colleague Dev runs the operational PostgreSQL database that the tracking website and the depot apps use.
Two screens matter to the business. The tracking page: a customer types one parcel number and sees every field of that parcel's scans. And the weekly weight report: the average parcel weight per depot per week, which reads only two columns of the table, but for every row.
In this mechanism Kavya meets five problems, one at the start of each article. Each problem looks like a bug: a slow lookup, a huge nightly job, a query that suddenly scans everything. Each one is actually a consequence of how bytes are placed next to each other. At the end of each article we go back and solve her problem with the mechanisms that part explained. All numbers in the story are illustrations.
The main idea of this whole mechanism:
How this mechanism is organized
- 1
Data at rest and the one ratio
What is different about data at rest, and which one ratio explains most of the cost?
CaseInstant on a laptop, seconds in the cloud
- 2
Layout models
Should a row, a column or a group of rows be the unit that sits together?
CaseThe 1.5-second tracking lookup
- 3
Mutation strategies
What physically happens when a value changes or is deleted, and who pays for it?
Case640 GB rewritten to change 1 MB
- 4
Physical clustering
How do we arrange rows so that whole units can be skipped without reading them?
CaseThe lookup that opens all 3,125 files
- 5
Access and media
What does one touch of the storage cost, and why does that set every constant above?
CaseSame code, three machines, three speeds
- 6
What physical organization forbids
What can no storage arrangement do, and what does the whole mechanism reduce to?
After that: a summary, hands-on labs, interview questions and a glossary.
One request: do not skip the formulas with numbers. Each article has one or two small formulas. The worked examples show that they are simple arithmetic, and that the results are very large or very small. The size of the result is the lesson.
What do I do, and what does each option cost?
Choose a layout
Data is about to be stored: row, columnar, PAX (columns within row groups) or key-value? Six questions, from the storage medium to the share of columns each query touches.
ChooseChoose a mutation strategy
Stored data changes: update in place, rewrite whole files (copy-on-write), or write changes separately and merge on read with delete vectors or equality deletes? Four questions, then the compaction interval and the metric that predicts failure.
ChooseChoose a clustering strategy
Rows can be physically ordered so queries skip most files: a single sort key, Z-order across a few dimensions, bucketing for joins, or no clustering at all? Two questions, then the checks that decide whether it pays its rent.
ChooseMeasure your medium
Before choosing any block, page or file size: measure your storage's latency, bandwidth and achievable concurrency, then derive the minimum efficient request and the queue depth your reads need.
Design
Can I see it myself?
The one ratio: bytes touched versus bytes needed on your own disk
Random 200-byte reads from a 1 GB file, as plain and as 4 KiB aligned reads with the page cache bypassed; then 1,000 small range GETs against 10 large ones from an S3-compatible store.
Real systemDocker, PythonRow versus column on the same table
10 million rows of 25 columns as CSV and as Parquet: file size, reading 2 columns, and reading one row by position.
MeasurePython with pandas, PyArrowCopy-on-write versus delete vector
5,000 row corrections on a table of 640,000-row files: copy-on-write rewrites against merge-on-read delta files, and scan time as deltas accumulate.
MeasurePython with DuckDB, PyArrowClustering payoff: sorted versus random files
20 million rows as 20 Parquet files, random and sorted by (depot_id, scan_time): files a one-depot query must open, then after one unsorted append.
MeasurePython with PyArrow, pandasThe composite-key rule (`C_a ≤ U`)
20 files sorted by (depot_id, scan_time) and by (user_id, scan_time): files a one-day query must open, alone and with a depot.
MeasurePython with PyArrow, pandasZ-order floor
A million 2-D points in 4,096 units: sorted on dimension 1, Z-ordered, and random; units a thin query on each dimension touches, and a 50/50 mix.
SimulatePython with NumPyQueue depth and the 0.7% reader
Random 4 KB reads from a 2 GB file with 1 to 128 requests in flight, page cache bypassed: IOPS against queue depth; then mmap.
MeasurePython
Can I reason about it under questioning?
Interview questions
Short questions on how data is laid out at rest, such as row and column layouts, updates and deletes, clustering and access paths, each with an answer you could give aloud and a link to the full explanation.
Scenarios
Longer problems on physical data layout, of the kind asked in senior interviews: each one estimated with numbers, diagnosed, and answered with its trade-offs.
The tensions this mechanism takes a position on. Each recurs across mechanisms.
Read-optimized vs. write-optimized
For example: B-tree vs. LSM, merge-on-read vs. copy-on-write.
Precompute vs. compute-on-read
For example: materialized views, ELT vs. ETL, schema-on-read.
Mutability vs. immutability
In-place update vs. append-only + GC; buys reproducibility and cheap concurrency, pays in space and janitorial work.
Early binding vs. late binding
When structure is committed; determines where the failure surfaces, at write time or read time.
Space vs. time
For example: compression, indexing, materialization, caching.
Show all 10 trade-offsShow fewer
Generality vs. specialization
For example: general engines vs. purpose-built structures.
Exactness vs. approximation
For example: sketches, sampling, ANN.
Coupling vs. coordination cost
For example: 2PC vs. sagas, schema contracts vs. schema-on-read.
Durability vs. acknowledgment latency
How much must be confirmed before you say "yes".
Cost as a substitutable resource
Money buys position on every other axis; the only axis denominated in a different unit.
Ideas this mechanism meets that keep coming back, mechanism after mechanism.
Fixed cost per operation
Fixed cost per operation implies a natural batch size.
Granularity mismatch
Granularity mismatch creates amplification.
Costs are conserved
Costs are conserved, never removed.
Independence is assumed
Independence is assumed everywhere and is true almost nowhere.
Immutable storage grows indirection
An immutable substrate grows an indirection layer plus a collector.