How Row Stores Lay Out Bytes

A row store writes one record after another. PostgreSQL 18 1,289 keeps each row as a tuple in an 8 kB page: a 23-byte header, an optional null bitmap, alignment padding, then the values in column order. CSV, JSON Lines and Avro 129 are row formats too. Three orders encoded as fixed-width binary show both layouts:

Three orders in row-major and column-major orderJavaScript
import struct
orders = [(1, 3, 40.2), (2, 3, 106.8), (3, 6, 21.3)]       # (order_id, book_id, total)
fmt = {"order_id": "<I", "book_id": "<B", "total": "<f"}    # 4 + 1 + 4 bytes, little-endian
def enc(field, value):
    return struct.pack(fmt[field], value).hex()
rows = [" ".join(enc(f, v) for f, v in zip(fmt, o)) for o in orders]
print("row-major:   ", " | ".join(rows))
cols = [" ".join(enc(f, v) for v in values) for f, values in zip(fmt, zip(*orders))]
print("column-major:", " | ".join(cols))
Output
row-major:    01000000 03 cdcc2042 | 02000000 03 9a99d542 | 03000000 06 6666aa41
column-major: 01000000 02000000 03000000 | 03 03 06 | cdcc2042 9a99d542 6666aa41

In row-major order, order 2 is one contiguous run of bytes, ideal for transactional (OLTP) work: insert an order, read a customer, update a status. The cost appears when a query needs one field of every record, because it must still read every other field in between.