Базы данных
Партиционирование таблиц
Cloudflare обрабатывает 1 триллион DNS запросов в месяц. Хранить такой объём в одной таблице невозможно. Решение: партиционирование по дням с автоматическим удалением через DROP PARTITION. Удаление дневного лога (миллиарды строк) занимает 50ms вместо часов.
- **Datadog**: TimescaleDB для 100+ миллиардов временных меток метрик в день с автоматическим compression
- **Cloudflare**: PostgreSQL range partitioning для DNS logs - DROP PARTITION вместо медленного DELETE
- **Zalando**: pg_partman для управления сотнями партиций в продакшне
Типы партиционирования
Партиционирование - разбиение большой таблицы на меньшие части (партиции) на одном сервере. В отличие от шардинга - нет network overhead, транзакции между партициями работают, JOIN возможен. Три основных типа: range (по диапазону), hash (по хешу), list (по списку значений).
PostgreSQL 10+ поддерживает declarative partitioning. TimescaleDB строится поверх неё для time-series: автоматически создаёт партиции (chunks) по времени и выполняет partition pruning. Это позволяет хранить 2+ лет данных и быстро удалять старые.
Какой тип партиционирования лучше для таблицы логов с частыми запросами "за последний месяц" и регулярным удалением данных старше года?
Range Partitioning в деталях
Range partitioning - наиболее популярный тип для time-series и архивных данных. Главное преимущество: partition detachment (DROP PARTITION) работает за O(1) - просто удаляет ссылку на файл данных. DELETE 100 миллионов строк может занять часы. DROP PARTITION - секунды.
Partition key constraints: в PostgreSQL все запросы по partition key автоматически направляются в нужную партицию. Но если partition key не в WHERE - полный scan всех партиций. Constraint exclusion: PostgreSQL автоматически исключает неподходящие партиции при наличии CHECK constraints.
Почему DROP PARTITION быстрее чем DELETE всех строк партиции?
Partition Pruning
Partition pruning - оптимизация планировщика запросов: при наличии условия на partition key в WHERE, PostgreSQL исключает нерелевантные партиции из плана. Запрос к таблице с 1000 партиций может читать только 1-2.
Runtime pruning (PostgreSQL 12+): partition pruning применяется не только при планировании но и при выполнении. Полезно для prepared statements и функций - partition key может быть параметром, определяемым в runtime.
Таблица секционирована по month(created_at). Запрос: WHERE EXTRACT(YEAR FROM created_at) = 2024. Сколько партиций будет просканировано?
PostgreSQL Partitioning в продакшне
PostgreSQL declarative partitioning (10+) поддерживает: глобальные индексы (уникальные constraint на всей таблице), foreign key на секционированные таблицы, DEFAULT партицию для данных без явной партиции. pg_partman - extension для автоматического управления партициями.
При добавлении строки с датой, для которой нет партиции, что происходит?
Когда партиционировать
Партиционирование добавляет сложность и overhead планировщика. Оно оправдано когда: таблица очень большая (100M+ строк), нужно регулярно удалять старые данные, большинство запросов содержат фильтр по partition key.
- 100M+ строк и медленные запросы по времени - кандидат для range partitioning
- Регулярное удаление старых данных (logs, events) - DROP PARTITION вместо DELETE
- Большинство запросов фильтруют по partition key - pruning сработает
- Архивирование: перенести старые партиции на cheaper storage (tablespaces)
Не стоит партиционировать: маленькие таблицы (<10M строк), когда запросы не фильтруют по partition key, когда нужно часто делать JOIN между партициями и другими таблицами (planner overhead растёт).
Партиционирование всегда ускоряет запросы
Партиционирование ускоряет запросы ТОЛЬКО если они содержат фильтр по partition key. Запросы без такого фильтра сканируют ВСЕ партиции - медленнее чем без партиционирования из-за overhead.
Плановщик PostgreSQL при большом количестве партиций (1000+) тратит больше времени на planning. Без pruning каждый запрос - overhead на анализ всех партиций.
Таблица transactions: 50M строк, INSERT 100,000/день, запросы типа "транзакции пользователя за всё время". Нужно ли партиционирование?
Ключевые идеи
- **Range partitioning по дате**: DELETE старых данных = DROP PARTITION (O(1) vs O(N))
- **Partition pruning**: работает только при прямом фильтре на partition key без функций
- **Когда НЕ нужно**: маленькие таблицы, запросы без фильтра по partition key, сложные JOIN
Связанные темы
Партиционирование - локальный аналог шардинга с другими trade-offs:
- Шардинг — Партиционирование = шардинг на одном сервере; шардинг = разные серверы
- Time-series БД — TimescaleDB добавляет автоматическое партиционирование и compression для PostgreSQL
- Индексы — Локальные и глобальные индексы на секционированных таблицах - важный trade-off
Вопросы для размышления
- Таблица messages: 10 млрд строк, 99% запросов по user_id, периодически чистить старые >1 года. Как партиционировать?
- Почему индексы на секционированных таблицах сложнее чем обычные? Что такое local vs global index?
- В каком сценарии партиционирование замедлит запросы вместо их ускорения?