Партиционирование
Предпосылки: таблицы и типы (CREATE TABLE), ограничения (PRIMARY KEY, UNIQUE).
← Ограничения | Представления →
Сервис аналитики записывает события: клики, просмотры, покупки. За год накопилось 500 млн строк — 120 ГБ данных. DELETE 50 млн старых строк — операция на десятки минут. Таблица не уменьшается: место от удалённых строк освобождается фоновым процессом, который сам нагружает систему.
Партиционирование решает эту проблему: вместо одной огромной таблицы — набор отдельных физических частей (партиций), разделённых по значению ключа. Удаление старых данных — DROP одной партиции, мгновенная операция.
Концепция
Логическая таблица остаётся одна — приложение пишет INSERT INTO events и SELECT FROM events как обычно. Физически данные лежат в отдельных частях, разделённых по значению ключа партиционирования (обычно дата или идентификатор региона). СУБД автоматически направляет вставку в нужную часть и при чтении пропускает части, заведомо не содержащие нужных строк. При 12 месячных партициях запрос по одному месяцу читает 1/12 данных.
Стратегии разделения
Таблицу можно разделить тремя способами. RANGE — по диапазону значений: данные за январь в одной части, за февраль — в другой. Самый частый выбор для дат и последовательных данных, потому что совпадает с типичным фильтром WHERE created_at BETWEEN ... AND ... и позволяет мгновенно удалять старые данные (DROP одной части за месяц).
LIST — по конкретным значениям: заказы из US в одной части, из EU — в другой. Подходит, когда ключ принимает фиксированный набор значений.
HASH — по хешу значения: строки равномерно распределяются по N частям. Используется, когда нет естественного ключа для RANGE или LIST, но нужно разделить нагрузку по I/O. Тот же принцип лежит в основе шардинга — разделения данных между несколькими серверами.
Цена
Запросы без фильтра по ключу партиционирования сканируют все части. SELECT * FROM events WHERE user_id = 42 без ограничения по дате — 36 отдельных сканов вместо одного.
Выбор ключа
Ключ партиционирования должен присутствовать в WHERE подавляющего большинства запросов. Если большинство запросов фильтрует по дате — партиционирование по дате. Если по customer_id — по customer_id. Но партиционирование по customer_id делает невозможным мгновенное удаление старых данных — данные всех дат перемешаны в каждой части, и удаление снова требует поштучного DELETE.
PostgreSQL реализует партиционирование через декларативный DDL (RANGE, LIST, HASH) — подробнее.
Sources
- PostgreSQL Documentation (v16): Partitioning. https://www.postgresql.org/docs/16/ddl-partitioning.html
← Ограничения | Представления →