Шардирование и партиционирование: как масштабировать базу
Продвинутое4 минРедакция Innovialabs
Коротко
Партиционирование делит большую таблицу на части внутри одного сервера, а шардирование распределяет данные по нескольким серверам по ключу шарда; начинайте с партиционирования и шардируйте, только когда один сервер действительно не справляется.
Коротко о разнице
Партиционирование делит одну логическую таблицу на несколько физических на том же сервере. Приложение работает с одной базой, а PostgreSQL сам кладёт строки в нужную партицию.
Шардирование распределяет данные по нескольким независимым серверам. Каждый шард хранит часть строк, выбранную по ключу шарда. Приложение или прокси-слой должны знать, где лежит каждая строка.
Партиционирование решает задачу «таблица слишком большая, её тяжело обслуживать». Шардирование решает задачу «одна машина не выдерживает объём записи или данных». Вторая проблема встречается гораздо реже и обходится гораздо дороже.
Партиционирование в PostgreSQL
В PostgreSQL есть декларативное партиционирование с тремя стратегиями.
| Стратегия | Как делятся строки | Типичное применение |
|---|---|---|
| Range | По диапазонам значений, например датам | Логи, события, заказы по месяцам |
| List | По перечню значений | Регион, страна, тип клиента |
| Hash | По хешу колонки по модулю N | Равномерное распределение, когда нет естественного диапазона |
Самый частый случай — range по времени:
CREATE TABLE events (
id bigint GENERATED ALWAYS AS IDENTITY,
created_at timestamptz NOT NULL,
payload jsonb,
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);
CREATE TABLE events_2026_10 PARTITION OF events
FOR VALUES FROM ('2026-10-01') TO ('2026-11-01');
List и hash выглядят похоже:
CREATE TABLE customers (...) PARTITION BY LIST (region);
CREATE TABLE customers_uz PARTITION OF customers FOR VALUES IN ('uz');
CREATE TABLE sessions (...) PARTITION BY HASH (user_id);
CREATE TABLE sessions_p0 PARTITION OF sessions
FOR VALUES WITH (MODULUS 4, REMAINDER 0);
Что это даёт на практике:
- Partition pruning: запрос с фильтром по ключу партиционирования читает только нужные партиции.
- Дешёвое удаление:
DETACH PARTITIONиDROP TABLEвместо огромногоDELETE, который раздувает таблицу. - Индексы меньше, а VACUUM и переиндексация идут быстрее.
Важные ограничения: первичный ключ и уникальные ограничения должны включать ключ партиционирования, а запросы без фильтра по нему всё равно затрагивают все партиции.
Горизонтальное шардирование
При шардировании каждый сервер хранит часть данных. Главное решение — ключ шарда:
- Хороший ключ: есть почти в каждом запросе, равномерно распределяет нагрузку, почти не меняется. Например,
tenant_idв B2B SaaS илиuser_idв пользовательском приложении. - Плохой ключ: мало уникальных значений (страна), перекос (один огромный клиент) или время для записи (все новые строки летят в последний шард).
Способы сопоставить ключ и шард:
- По хешу: равномерно, но при добавлении шардов данные приходится переносить, если не использовать много виртуальных бакетов.
- По диапазону: просто, но легко получить горячие точки.
- Справочник (lookup-таблица): гибко, но нужен дополнительный сервис поиска.
Инструменты вроде Citus для PostgreSQL или Vitess для MySQL берут на себя маршрутизацию, но ограничения дизайна никуда не исчезают.
Межшардовые запросы: настоящая цена
Как только данные разнесены, всё, что затрагивает несколько шардов, усложняется:
- JOIN между шардами требует совместного размещения (связанные таблицы шардируются по одному ключу) или склейки в приложении.
- Агрегации (
COUNT,SUM, отчёты) нужно отправлять на все шарды и объединять результаты. - Транзакции между шардами требуют двухфазного коммита или паттерна saga — и новых сценариев отказа.
- Уникальность и последовательности перестают быть глобальными по умолчанию.
Поэтому после шардирования аналитику обычно выносят в отдельное хранилище данных.
Когда это преждевременно
Партиционирование рано, если:
- Таблица спокойно помещается в память, а запросы быстрые при правильных индексах.
- Большинство запросов не фильтрует по естественному ключу партиционирования.
Шардирование рано, если вы ещё не попробовали:
- Оптимизацию запросов и индексов, пул соединений.
- Вертикальное масштабирование и реплики для чтения.
- Партиционирование и архивирование старых данных.
- Вынос аналитики с основной базы.
Разумный порядок: оптимизировать, увеличить сервер, добавить реплики, партиционировать, потом шардировать.
Частые ошибки
- Выбирать ключ шарда только под сегодняшние запросы, не думая о будущих отчётах.
- Создавать тысячи мелких партиций — планировщик начинает работать медленнее.
- Забывать заранее создавать будущие партиции, и вставки падают в начале нового месяца.
- Шардировать, чтобы «починить» медленные запросы, хотя причина была в отсутствующих индексах.