Lesson 5 · Internals & data structures

Storage: pages, heap & tuples

What a table actually is on disk — and why "one row = one tuple" is a lie.

Your win: explain how Postgres physically stores a table's rows, so MVCC, indexes, and VACUUM (the rest of this part) all have somewhere to stand.

A table is a file of 8 KB pages

Physically, a table (the heap) is a sequence of fixed-size pages — 8 KB each.1 The page is the unit of disk I/O and of the in-memory buffer cache: Postgres always reads and writes whole pages, never a single row.

one 8 KB page ┌───────────────────────────────────────────┐ │ page header (24 bytes) │ │ item pointers →→→ (grow downward) │ │ ↓ free space │ │ ↑ │ │ tuples ←←← (grow upward from the bottom) │ └───────────────────────────────────────────┘

Item pointers (line pointers) grow down from the top; tuples grow up from the bottom; free space sits between. A tuple's physical address is its ctid = (page number, item pointer).

A tuple is a row version

The lie to unlearn A tuple is not "a row" — it's a physical version of a row (header + null bitmap + column data). One logical row can be represented by several tuples at once, only one of which is currently visible. That single fact is the whole foundation of MVCC (next lesson).
Anchor Your emails table is exactly this: a heap of 8 KB pages, each holding many email tuples. The subject, status, and resource_path live inline in the tuple; a large content jsonb may be moved out of the page (Lesson 7, TOAST). Every soft-delete UPDATE will add a new tuple for the same email_id (Lesson 6).
Read this next

The Internals of PostgreSQL — heap file & tuple structure

Diagram-rich walkthrough of page layout and tuples; pair with the official page-layout reference for exact bytes.

interdb.jp/pg — heap table layout
postgresql.org/docs — Database Page Layout

Check yourself (from memory)

Q1. A Postgres table is physically stored as…

The heap = a sequence of 8 KB pages, the unit of I/O and buffer cache. Tuples live inside pages.

Q2. Within a page, tuples are…

A tuple is a physical row version (header + null bitmap + data), addressed by its ctid.

Q3. One logical row can correspond to…

Multiple versions can coexist — the foundation of MVCC. Only one is visible to a given transaction.
How does Postgres physically store a table's rows?
recall, then click to reveal
A table (the "heap") is a sequence of fixed-size 8 KB pages — the unit of disk I/O and buffer cache. Each page has a header, an array of item pointers (line pointers) growing down from the top, and tuples growing up from the bottom, with free space between. A tuple is a physical row version (header + null bitmap + column data), addressed by its ctid = (page number, item pointer). Crucially, one logical row can have multiple tuple versions on disk — the basis of MVCC.
Want to actually see it — SELECT ctid, * FROM emails, or the pageinspect extension? Ask me.

1. PostgreSQL — Database Page Layout; The Internals of PostgreSQL.