page 08 kBpage 18 kBpage 28 kBWALslot 0emptyslot 1emptyslot 2empty1CREATE TABLE books (2 id integer, name text,3 stock integer);4INSERT INTO books VALUES5 (1, 'Dune', 3),6 (2, 'Coraline', 5),7 (3, 'Holes', 2);8SELECT ctid, * FROM books;9UPDATE books SET stock = 110 WHERE id = 3;free spaceheader · 24 byteslp 1 → 8152lp 2 → 8104lp 3 → 8064lp 4 → 8024(0,4) Holes · stock 1@8024 · 40 B(0,3) Holes · stock 2@8064 · 40 B(0,2) Coraline · stock 5@8104 · 44 B(0,1) Dune · stock 3@8152 · 40 Bpd_lower24pd_upper8192free8168pg 0
Back row: the file on disk, the WAL, and shared buffers in memoryFront: the SQL, the header numbers, and page 0 opened upMoving: the page between disk and memory, and a WAL recordAt the back: the books file on disk (a row of 8 kB pages), the write-ahead log, and shared buffers in memory. In front: the SQL, the page header's numbers, and page 0 opened up. Line pointers grow down from the header and rows stack up from the end of the page; the free space between them shrinks. A row's address, its ctid, is (page, line pointer). An update writes a new row version; the changed page is logged in the WAL before it goes back to disk. The page in the figure is not to scale: the header and these 3 rows use only 164 of its 8,192 bytes.

Pages on disk

  1. PostgreSQL stores the books table as one file on disk, cut into pages of 8,192 bytes (8 kB). A page is the unit of work: the database reads and writes whole pages, never a single row.

Inside a page

Changing a page

What happens

;; step 1/9 · pages
table books  ->  one file on disk
the file is a row of pages, 8192 bytes each
  page 0   bytes      0 ..  8191
  page 1   bytes   8192 .. 16383
  page 2   bytes  16384 .. 24575
read or write one row?  move its whole page

The page in the figure is not to scale: the header and these 3 rows use only 164 of its 8,192 bytes. Row sizes assume a usual 64-bit build, where rows start on 8-byte boundaries, and short text values (under 127 bytes) with a 1-byte length header. Long values are moved out of the page (TOAST).