У чому різниця між шардуванням та партиціонуванням
Обидва терміни означають розбиття великих обсягів даних на частини, але вони працюють на принципово різних рівнях архітектури:
🟢 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 партицій за годинами» (Уб’є планувальник СУБД і пам’ять блокувань).