Same table, two on-disk shapes — row-store pages versus per-column .bin files
A row store interleaves all columns of one row contiguously on a page; a column store stores all values of one column contiguously in its own file — same rows, rotated 90 degrees.
If both engines hold the same 1.2 billion rows, the only thing that can explain a 9000x gap is what each engine actually pulls off disk — so we need to look at the physical layout under the rows. Here's that physical layout: same rows, two arrangements.
Scene 02
Same table, two on-disk shapes
- Watch
- Try it
- Predict
- Capture
Same five rows, two layouts. The row strip is the page on disk — every column of every row glued together. The four .bin strips are the column files — each one holds one column end-to-end. SELECT avg(latency_ms) lights latency_ms in both panels: the row store sweeps every tile anyway; the column store streams exactly one file.
Highlighted lines are the ones running in the diagram right now.
def scan(query):page = openPage('events.page')out = []for row_offset in page.row_offsets: # every rowrow = []for col in SCHEMA: # all 4 columns, every timebytes = page.read(row_offset, col.width)if col.name in query.columns:row.append(decode(bytes, col.type))row_offset += col.width # skip past unwantedout.append(row)return out
def scan(query):streams = [open(f'{c}.bin') for c in query.columns]if len(streams) == 1:return list(streams[0]) # zero-waste single streamreturn zipByOrdinal(streams) # tuple reconstruction
def zipByOrdinal(streams):out = []for k in range(rowCountInPart()):# value at position k in each .bin belongs to row k.# no row_id stored; alignment is purely by index.row = tuple(stream.readAt(k) for stream in streams)out.append(row)return out
Where this sits in Build a columnar OLAP store (ClickHouse / Druid style)
Scene 02 of 13, in the Why columnar? act — The 30-min Postgres query vs the 200ms ClickHouse query — what's on disk?. Row store interleaves a row's columns contiguously; column store stores each column in its own file. Same rows, rotated 90°.
Up next. Once adjacent bytes on disk are the same column — same type, often similar values — compression that fails on row pages suddenly works. Let's see how much.
All 13 scenes in Build a columnar OLAP store (ClickHouse / Druid style) · Every curriculum