В чём разница между шардированием и партиционированием
Оба термина означают разбиение больших объёмов данных на части, но они работают на принципиально разных уровнях архитектуры:
🟢 Junior Level
Оба термина означают разбиение больших объёмов данных на части, но они работают на принципиально разных уровнях архитектуры:
Партиционирование (Внутри ОДНОГО сервера):
┌────────────────────────────────────────────────────────┐
│ Один физический сервер СУБД │
│ Таблица "orders" │
│ ├── orders_2023 (файл на диске A) │
│ ├── orders_2024 (файл на диске B) │
│ └── orders_2025 (файл на диске C) │
│ [Общий CPU, общая RAM, один движок ACID-транзакций] │
└────────────────────────────────────────────────────────┘
Шардирование (Между РАЗНЫМИ серверами):
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ Сервер 1 │ │ Сервер 2 │ │ Сервер 3 │
│ (Шард 1) │ │ (Шард 2) │ │ (Шард 3) │
│ Users: A - H │ │ Users: I - P │ │ Users: Q - Z │
│ [Свои CPU/RAM] │ │ [Свои CPU/RAM] │ │ [Свои CPU/RAM] │
└────────────────┘ └────────────────┘ └────────────────┘
Простое правило различия:
- Партиционирование (Partitioning / Секционирование): Разделение таблицы на отдельные файлы/секции внутри одного инстанса базы данных. Используется для ускорения запросов и мгновенного удаления старых данных.
- Шардирование (Sharding): Разделение данных между множеством независимых физических серверов (Shared-Nothing Architecture). Используется, когда данные или нагрузка физически не помещаются на один компьютер.
🟡 Middle Level
1. Детальное сравнительное сопоставление
| Критерий | Партиционирование (Partitioning) | Шардирование (Sharding) |
|---|---|---|
| Физическая инфраструктура | Один сервер СУБД (общие CPU, RAM, дисковый контроллер) | Кластер независимых серверов (у каждого свои CPU, RAM, диски) |
| ACID-транзакции | Поддерживаются СУБД из коробки без ограничений | Требуют распределенных протоколов (2PC) или Saga |
| Внешние ключи (Foreign Keys) | Работают внутри базы данных | Не поддерживаются между шардами на уровне СУБД |
| Сложность внедрения | Низкая: настраивается через DDL-команды SQL (PARTITION BY) |
Высокая: требует координатора, прокси, роутинга и перебалансировки |
| Главная цель | Оптимизация чтения (Partition Pruning) и обслуживание архивов | Горизонтальное масштабирование записи и хранилища |
2. Реализация партиционирования в PostgreSQL
Начиная с PostgreSQL 10, поддерживается декларативное партиционирование:
-- 1. Создаем родительскую партиционированную таблицу
CREATE TABLE audit_logs (
id BIGINT GENERATED ALWAYS AS IDENTITY,
service_name VARCHAR(64) NOT NULL,
payload JSONB,
created_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (id, created_at) -- Ключ партиционирования ОБЯЗАН входить в Primary Key!
) PARTITION BY RANGE (created_at);
-- 2. Создаем физические секции (партиции)
CREATE TABLE audit_logs_2026_q1 PARTITION OF audit_logs
FOR VALUES FROM ('2026-01-01 00:00:00+00') TO ('2026-04-01 00:00:00+00');
CREATE TABLE audit_logs_2026_q2 PARTITION OF audit_logs
FOR VALUES FROM ('2026-04-01 00:00:00+00') TO ('2026-07-01 00:00:00+00');
3. Механизм Partition Pruning (Отсечение партиций)
Главный выигрыш партиционирования — оптимизатор СУБД при выполнении запроса:
EXPLAIN ANALYZE
SELECT * FROM audit_logs
WHERE created_at >= '2026-02-01' AND created_at < '2026-03-01';
Планировщик автоматически исключает из сканирования партицию audit_logs_2026_q2 и все остальные. Он обращается только к компактному файлу audit_logs_2026_q1, читая в разы меньше страниц с диска и удерживая лишь нужные индексы в оперативной памяти.
🔴 Senior Level
1. Архитектурная шкала масштабирования БД
Прежде чем прибегать к дорогому и сложному шардированию, инженеры проходят строгую цепочку эволюции:
[1. Индексы и тюнинг SQL]
│ (Исчерпали эффективность)
▼
[2. Партиционирование таблиц] ──► Ускоряет локальные выборки, облегчает VACUUM
│ (Один сервер не тянет чтение)
▼
[3. Read Replicas (Репликация)] ──► Разгружает чтение (SELECT) через пул реплик
│ (Один Master не справляется с потоком INSERT/UPDATE или диск переполнен)
▼
[4. Шардирование (Sharding)] ──► Разделение записи на независимые Master-узлы
2. Мгновенная очистка данных: DROP TABLE vs DELETE
Почему партиционирование незаменимо для логов, событий и временных рядов (Time-Series data)?
- Обычная таблица (
DELETE): ЗапросDELETE FROM logs WHERE created_at < '2025-01-01'для 100 миллионов строк:- Генерирует гигантский объём WAL-логов (запись каждого факта удаления).
- В PostgreSQL строки не удаляются физически, а помечаются как Dead Tuples, вызывая табличный Bloat (раздувание) и парализуя базу долгим процессом
VACUUM. - Блокирует страницы и пожирает дисковые IOPS часами.
- Партиционированная таблица (
DROP PARTITION): КомандаDROP TABLE logs_2024_q4выполняется за 10 миллисекунд: СУБД просто удаляет соответствующий файл с файловой системы ОС без генерации строк в WAL и без необходимости запускать VACUUM.
3. Композитное секционирование (Hybrid Architecture)
В крупных высоконагруженных enterprise-системах оба подхода объединяются:
- Шардирование верхнего уровня: База шардируется по
tenant_id(организация) илиuser_idпо 16 серверам. - Партиционирование внутри каждого шарда: На каждом из 16 серверов таблицы заказов партиционируются по месяцам (
created_at).
4 Tricky Questions
1. Почему в PostgreSQL в первичный ключ (PRIMARY KEY) партиционированной таблицы обязаны входить все колонки ключа партиционирования?
Ответ:
Каждая партиция в PostgreSQL является независимой таблицей со своим собственным B-Tree индексом первичного ключа. В PostgreSQL нет глобальных индексов (Global Indexes), охватывающих сразу все партиции.
Если бы мы создали PRIMARY KEY (id), а партиционировали по created_at, то при вставке строки с id = 100 в партицию за 2026 год база не смогла бы проверить, существует ли уже строка с таким же id = 100 в партиции за 2025 год, без синхронного поиска по индексам всех партиций системы (что убило бы производительность вставки). Поэтому уникальность гарантируется только локально внутри партиции, требуя PRIMARY KEY (id, created_at).
2. В чем опасность создания слишком большого количества партиций (например, 10 000 партиций по дням)?
Ответ:
- Накладные расходы планировщика: При генерации плана запроса PostgreSQL обязан открыть и проверить метаданные каждой партиции в системном каталоге (
pg_class), что приводит к экспоненциальному росту времени компиляции запроса (Planning Time превышает Execution Time в десятки раз). - Исчерпание памяти и дескрипторов: Каждая открытая партиция удерживает файловые дескрипторы и блокировки в разделяемой памяти (
Lock Tables/max_locks_per_transaction), вызывая падение запросов с ошибкой out of shared memory. Правило: В одной таблице не рекомендуется держать более нескольких сотен партиций.
3. В какой момент партиционирование исчерпывает свои возможности и требует перехода к шардированию?
Ответ: Партиционирование перестает помогать, когда бутылочным горлышком становятся физические ресурсы одного сервера:
- Насыщение дискового контроллера по записи: Поток
INSERT / UPDATEпревышает максимальный IOPS и пропускную способность дисковой подсистемы (NVMe/RAID). - Время бэкапа и восстановления (RTO/RPO): Создание резервной копии базы объемом 10–20 ТБ (
pg_dumpили LVM snapshot) и ее восстановление при аварии начинает занимать сутки, нарушая SLA бизнеса. - Лимиты CPU/RAM: База уперлась в потолок максимальной доступной конфигурации сервера в облаке.
4. В чем разница между Plan-Time и Run-Time Partition Pruning?
Ответ:
- Plan-Time Pruning: Выполняется на этапе компиляции SQL, когда значения предикатов известны статически (константы:
WHERE created_at >= '2026-01-01'). Планировщик сразу отсекает ненужные партиции до начала выполнения. - Run-Time Pruning: Выполняется в момент исполнения запроса, если фильтр зависит от параметров prepared statements, подзапросов или параметров функций (например,
WHERE created_at >= now() - INTERVAL '1 day'). СУБД динамически отсекает партиции после вычисления значения выражения во время исполнения.
🎯 Шпаргалка для интервью
Главное:
- Партиционирование — деление таблицы внутри одного сервера СУБД.
- Шардирование — деление данных между разными серверами кластера.
- Партиционирование сохраняет полноценные ACID-транзакции, внешние ключи (
FK) и управляется через стандартный SQL. - Шардирование требует распределенной координации, ломает межшардовые Foreign Keys и ACID.
- Partition Pruning — ключевой механизм ускорения: база читает только релевантные партиции, игнорируя остальные.
DROP TABLE partition_nameудаляет гигабайты старых данных за миллисекунды без блокировок и без MVCC dead tuples / bloat.- Порядок масштабирования: Индексы $\to$ Партиционирование $\to$ Read Replicas $\to$ Шардирование.
Красные флаги (Чего говорить НЕЛЬЗЯ):
- ❌ «Партиционирование позволяет масштабировать запись на 10 серверов» (Партиционирование всегда ограничено одним сервером).
- ❌ «Для очистки старых логов в партиционированной таблице нужно запускать
DELETE» (Нужно вызыватьDROP TABLE partition_nameилиDETACH PARTITION). - ❌ «Можно нарезать таблицу на 50 000 партиций по часам» (Убьет планировщик СУБД и память блокировок).