PostgreSQL вирівнює значення колонок у рядку за межами їхнього типу (alignment): bigint, timestamptz, float8 - за 8 байтами, int - за 4, smallint - за 2, boolean і text - за 1. Між колонками вставляються «порожні» байти (padding), щоб наступне значення почалося з правильної адреси.
Приклад:
-- Невдалий порядок
CREATE TABLE events_bad (
is_processed boolean, -- 1 байт + 7 байтів вирівнювання
created_at timestamptz, -- 8
priority smallint, -- 2 + 6 байтів вирівнювання
user_id bigint, -- 8
attempts int -- 4
);
-- Той самий набір колонок, від більших до менших
CREATE TABLE events_good (
created_at timestamptz, -- 8
user_id bigint, -- 8
attempts int, -- 4
priority smallint, -- 2
is_processed boolean -- 1
);
У першій таблиці рядок займає помітно більше місця через вирівнювання. На мільярді рядків різниця - десятки гігабайтів на диску, в кеші й у бекапах.
Правило: колонки фіксованої довжини - від найбільшого вирівнювання до найменшого (8 → 4 → 2 → 1 байт), а змінної довжини (text, numeric, jsonb) - у кінці.
Що ще впливає на розмір рядка:
- Заголовок рядка - ~23 байти на кожен рядок незалежно від даних. Тому мільярд вузьких рядків - це вже десятки гігабайтів лише заголовків.
NULLне займають місця для значення - лише біт у bitmap.- Короткі рядки змінної довжини (до 126 байтів) мають 1-байтовий заголовок замість 4.
Як перевірити: pg_column_size(row(...)) і pg_relation_size() до й після.
Коли це варто робити: великі таблиці з мільйонами й мільярдами рядків - події, метрики, журнали. Для звичайних таблиць виграш непомітний. Переставити колонки в наявній таблиці можна лише перестворивши її, тож про порядок думають під час проєктування.