Как реализовать партиционирование данных
Что проверяют
Отличаете ли вы партиционирование внутри одной базы от шардирования по серверам и знаете ли, что оно меняет для приложения. Следом обычно спрашивают, как вы будете удалять старые данные и что станет с первичным ключом.
Как отвечать
Партиционирование делит одну логическую таблицу на физические части по ключу. Приложение работает с родительской таблицей, а база сама раскладывает строки. В PostgreSQL для этого есть декларативный синтаксис:
- по диапазону:
PARTITION BY RANGE (created_at), партиция на месяц. Самый частый вариант для заказов, логов, событий; - по списку:
PARTITION BY LIST (region), когда значений немного и они известны заранее; - по хешу:
PARTITION BY HASH (customer_id), равномерное распределение там, где естественного диапазона нет.
Выигрыш даёт отсечение партиций (partition pruning): запрос с условием по ключу читает только нужные части. Второй выигрыш в обслуживании: старый месяц удаляется через DETACH PARTITION и DROP TABLE мгновенно, вместо DELETE на миллионы строк с последующим раздуванием таблицы.
Что меняется на стороне Java. Сущность в JPA мапится на родительскую таблицу, Hibernate о партициях не знает. Но первичный ключ и уникальные ограничения обязаны включать ключ партиционирования, поэтому ключ становится составным, (id, created_at), а в сущности появляется @IdClass или @EmbeddedId. В репозиториях каждый частый запрос должен фильтровать по created_at, иначе отсечения не будет.
Новые партиции должны появляться заранее: расширение pg_partman, задача по расписанию или миграция Flyway. Партиция DEFAULT страхует от ошибки вставки, но копить в ней данные нельзя.
Где спотыкаются
Путают с шардированием. Партиции живут на одном сервере и не добавляют ни CPU, ни диска: это инструмент управления большой таблицей, а не горизонтального масштабирования.
Не учитывают ограничение на уникальность. Глобально уникальный id без ключа партиционирования в PostgreSQL не объявить, и это всплывает уже после миграции.
Нарезают слишком мелко. Тысячи партиций замедляют планирование каждого запроса, а таблице на пару миллионов строк партиционирование обычно не нужно.