Партиционирование таблиц на собеседовании Data Engineer
Карьерник — Duolingo для аналитиков: 10 минут в день тренируй SQL, Python, A/B, статистику, метрики и ещё 3 темы собеса. 1500+ вопросов в Telegram-боте. Бесплатно.
Содержание:
Зачем спрашивают на собесе DE
Партиционирование — основной инструмент управления big-tables в DWH. На собесе DE обязательно: «как партиционировать events на 10ТБ», «что такое partition pruning», «когда range, а когда hash». Уровень middle/senior — детали partition-wise join и многомерного партиционирования.
Главная боль без понимания — DE не партиционировал, таблица 5ТБ, ETL-вставка по 10М строк/час начала падать на VACUUM, downstream-аналитика тормозит.
Типы партиционирования
Range — диапазон значений. Чаще всего по дате.
CREATE TABLE events (
id BIGSERIAL,
user_id BIGINT,
event_at TIMESTAMP NOT NULL,
payload JSONB
) PARTITION BY RANGE (event_at);
CREATE TABLE events_2026_05 PARTITION OF events
FOR VALUES FROM ('2026-05-01') TO ('2026-06-01');
CREATE TABLE events_2026_06 PARTITION OF events
FOR VALUES FROM ('2026-06-01') TO ('2026-07-01');Идеально для time-series: append текущей партиции, drop старых.
List — фиксированные значения. Для категориальных ключей с известным набором.
CREATE TABLE orders (...) PARTITION BY LIST (country);
CREATE TABLE orders_ru PARTITION OF orders FOR VALUES IN ('RU');
CREATE TABLE orders_kz PARTITION OF orders FOR VALUES IN ('KZ');
CREATE TABLE orders_other PARTITION OF orders DEFAULT; -- catch-ALLHash — равномерное распределение по hash-функции. Полезно, когда natural key неравномерный (90% строк с одним user_id) или нужно равномерное распараллеливание.
CREATE TABLE events (...) PARTITION BY HASH (user_id);
CREATE TABLE events_p0 PARTITION OF events FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE events_p1 PARTITION OF events FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE events_p2 PARTITION OF events FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE events_p3 PARTITION OF events FOR VALUES WITH (MODULUS 4, REMAINDER 3);Минус: range/list-фильтры не отсекают партиции, только фильтр по hash-ключу.
Partition pruning
Главная фича: планировщик исключает партиции из сканирования по фильтру.
SELECT count(*) FROM events WHERE event_at >= '2026-05-01' AND event_at < '2026-06-01';
-- читает только events_2026_05В EXPLAIN это видно по факту — нет partitions исключённых.
Условия для pruning:
- Partition key явно в
WHERE(или derivable:event_at::date = '2026-05-15'обычно работает, но безопаснееevent_at >= ... AND event_at < ...). - Partition key — значение или диапазон, не функция.
- Postgres ≥ 11 поддерживает execution-time pruning — отсекает партиции по параметрам prepared statement.
Не работает:
WHERE date_part('month', event_at) = 5— функция от ключа.- Join по non-partition колонке без явного partition key в фильтре.
- Subquery, который планировщик не может развернуть.
Partition-wise join и aggregate
Partition-wise join. Если две таблицы партиционированы одинаково по join-ключу, Postgres может джойнить попартиционно.
SET enable_partitionwise_join = ON;
SELECT *
FROM orders o JOIN order_items oi ON o.id = oi.order_id
WHERE o.created_at >= '2026-05-01';
-- если обе партиционированы по created_at одинаково — join попартиционноСнижает потребление памяти и параллелит работу.
Partition-wise aggregate.
SET enable_partitionwise_aggregate = ON;
SELECT user_id, count(*) FROM events GROUP BY user_id;
-- агрегация per partition + final mergeВ Postgres дефолт — OFF (память дороже). На big-data DWH-нагрузке включай.
Когда партиционировать
По метрикам. Партиционирование оправдано, когда таблица:
100 ГБ или > 1 млрд строк (порог субъективный, но обычно ниже выгод не хватает).
- Регулярно фильтруется по партиционному ключу.
- Имеет жёсткий TTL (старые данные удаляются — drop партиции вместо
DELETE).
Какой ключ.
- Time-series, логи, события → range по
event_at(день/неделя/месяц по объёму). - Multi-tenant с равномерной нагрузкой → hash по
tenant_id. - Геолокальные/региональные данные → list по
country/region. - Жёсткие compliance-требования (GDPR, 152-ФЗ) → list по
regionдля локализации.
Что лучше — много мелких или мало крупных. Дефолт — каждая партиция несколько ГБ. Слишком много мелких → накладные расходы планировщика, slow queries on metadata. Слишком крупные → теряешь выгоду pruning.
Sub-partitioning и многомерное
В Postgres можно делать вложенные партиции.
CREATE TABLE events (...) PARTITION BY RANGE (event_at);
CREATE TABLE events_2026_05 PARTITION OF events
FOR VALUES FROM ('2026-05-01') TO ('2026-06-01')
PARTITION BY HASH (user_id);
-- внутри hash-сабпартицииПрименяется редко: операционно тяжело. Если нужен такой паттерн — это часто признак, что вы хотите ClickHouse / GP с distribution + partitioning из коробки.
В ClickHouse partitioning + ORDER BY работают независимо: партиция = физический сегмент данных, sorting key — порядок внутри партиции, primary key (sparse index) — поиск.
CREATE TABLE events (...)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_at)
ORDER BY (user_id, event_at);В Greenplum: distribution — по узлам, partitioning — внутри узла. Ключ распределения и ключ партиционирования — разные.
Грабли и операционные нюансы
Constraint exclusion устарел. В Postgres 10 был constraint_exclusion, дефолтное partitioning через inheritance. С 11+ — declarative partitioning, оно лучше. Старые ответы на «как партиционировать» с INHERITS и триггерами уже неактуальны.
Drop партиции — мгновенный. В отличие от DELETE WHERE created_at < ..., который порождает массу dead tuples и тяжёлый VACUUM.
ALTER TABLE events DETACH PARTITION events_2024_01;
DROP TABLE events_2024_01;
-- или одной командой через ALTER TABLE ... DROP PARTITIONAdd партиции автоматически. pg_partman или собственный cron — иначе INSERT в неподготовленную партицию упадёт. Ловушка проявляется в воскресенье в 23:59.
Foreign keys в партиционированных. До PG 11 — нельзя FK на партиционированную. С 12+ — можно. Но FK с партиционированной на партиционированную — нюансы.
Глобальные индексы. В Postgres нельзя сделать UNIQUE индекс по non-partition колонке. Если нужен глобальный unique — partition key должен быть в колонках индекса.
COPY в партиционированную. Работает, но routing на каждой строке — медленнее, чем COPY в конкретную партицию.
Частые ошибки
Партиционировать всё подряд. Маленькие таблицы (< 10ГБ) — нет смысла, накладные расходы перевесят выгоду.
Range по высокой кардинальности. Партиционировать по created_at с 2-минутным шагом → миллионы партиций → каталог пухнет.
Забыть DEFAULT партицию для list. INSERT с неизвестной категорией упадёт. Лучше сделать catch-all default.
Партиционировать без pruning-фильтра. Если запросы не фильтруют по partition key — партиционирование не помогает, только мешает.
Не настроить автосоздание. Будущая партиция не существует — INSERT падает. pg_partman или cron-скрипт обязательны.
Делать DELETE вместо DROP partition. Полная боль на 100ГБ. DROP партиции — мгновенно.
Игнорировать perf-тюнинг. Включи enable_partitionwise_join и enable_partitionwise_aggregate для DWH-нагрузок.
Партиционировать по UUID. UUIDv4 случайны → hash работает, но pruning по диапазону невозможен. Range по UUID почти бесполезен.
Связанные темы
- Партиционирование в ClickHouse для DE
- Индексы БД на собесе DE
- EXPLAIN и план запроса для DE
- Star schema vs Snowflake для DE
- Подготовка к собесу Data Engineer
FAQ
Партиционирование увеличивает скорость SELECT всегда?
Нет. Только если запрос фильтрует по partition key и pruning работает. Без фильтра запрос читает все партиции, что медленнее, чем одна большая таблица.
Можно ли менять partition key потом?
Не напрямую. Нужно создать новую партиционированную таблицу, перелить данные, переключить.
Как мониторить размер партиций?
pg_total_relation_size('partition_name') или pg_relation_size. Удобно через \dt+ и view pg_partition_tree.
Hash партиционирование сохраняет порядок?
Нет. Hash распределяет случайно. Запросы с ORDER BY user_id не получат бонуса от такого партиционирования.
Партиционирование в OLTP оправдано?
Реже, чем в DWH. Но для логов / истории / событий пользователя — да. Главное — partition key должен быть в большинстве WHERE.
Это официальная информация?
Нет. Статья основана на документации Postgres 14+, ClickHouse 23.x и опыте эксплуатации DWH.
Тренируйте Data Engineering — откройте тренажёр с 1500+ вопросами для собесов.