📫 Пост 46. Партиционирование БД - как база понимает, куда идти за данными?
Окей, таблицу разделили на части. А как БД понимает, в какой именно партиции лежат нужные данные? Короче об этом пост)
🧠 Партиции - это не просто отдельные таблицы Многие представляют партиционирование так: orders_2023 orders_2024 orders_2025 И думают, что сервис сам должен решать, в какую таблицу писать Нет 🙂 Для сервиса по-прежнему существует одна таблица: orders А все остальное - забота СУБДшки
⚙️ Как база понимает, куда положить новую запись? Когда создается партиционированная таблица, обязательно указывается ключ партиционирования. Например: PARTITION BY RANGE (created_at) или PARTITION BY HASH (user_id) или PARTITION BY LIST (country) То есть мы говорим базе - разделяй данные по этому столбцу После этого создаются сами партиции. Например: orders_2023 VALUES FROM ('2023-01-01') TO ('2024-01-01') orders_2024 VALUES FROM ('2024-01-01') TO ('2025-01-01') То есть диапазоны задаются явно. База хранит эту информацию в своих системных метаданных.
📦 Что происходит при INSERT? Допустим приходит запись: created_at = '2024-08-15' СУБД делает очень простую вещь: 1️⃣ смотрит ключ партиционирования 2️⃣ видит значение 3️⃣ сравнивает его с диапазонами 4️⃣ автоматически выбирает нужную партицию В итоге запись сразу попадет в: orders_2024 Разработчик вообще об этом не думает.
🔍 А что происходит при SELECT? Допустим пишем запрос: SELECT * FROM orders WHERE created_at >= '2024-06-01' Что делает база? Она анализирует условие. Видит, что нужны только данные за 2024 год И понимает: orders_2023 ❌ orders_2024 ✅ orders_2025 ❌ Поэтому читает только одну партицию. Это называется Partition Pruning. Именно благодаря этому запросы становятся быстрее.
🤔 А если написать запрос без условия? Например: SELECT * FROM orders Тогда база пройдет по всем партициям. Фактически получится что-то вроде: orders_2023 UNION ALL orders_2024 UNION ALL orders_2025 Только делает это сама СУБД.
🔗 А как работают JOIN? Вот здесь многие думают, что все ломается. На самом деле нет. Можно спокойно написать: SELECT * FROM orders o JOIN users u ON o.user_id = u.id Если таблица orders партиционирована, PostgreSQL сам решит: какие партиции нужно читать. Для сервиса ничего не меняется.
⚠️ Но есть нюанс Если запрос не использует ключ партиционирования, база может открыть все партиции. Например: SELECT * FROM orders WHERE status = 'PAID' Если партиционирование было по created_at, то определить нужную партицию невозможно. Придется искать во всех. Поэтому очень важно правильно выбирать ключ партиционирования.
📚 А как работают индексы? Тоже популярный вопрос. Ответ зависит от СУБД. Но чаще всего (например, PostgreSQL): 👉 индекс создается на каждой партиции отдельно. Например: orders_2023 - index_user_id
orders_2024 - index_user_id
orders_2025 - index_user_id То есть у каждой партиции свой собственный индекс.
🤔 А если создать индекс на родительской таблице? Начиная с PostgreSQL 11 можно написать: CREATE INDEX idx_user ON orders(user_id); Но физически база все равно создаст отдельный индекс для каждой партиции. Это называется partitioned index. То есть индекс выглядит единым только логически.
🚀 Почему это быстрее? Представь таблицу на миллиард строк. Один огромный индекс тоже будет огромным. А теперь разделим данные на 10 партиций. Получится: 10 индексов по 100 миллионов записей. Работать с ними намного быстрее.
⚡ А если добавить новую партицию? Например, наступил 2026 год. Создаем: orders_2026 Все. Теперь новые записи автоматически начнут попадать туда. Никакой код сервиса менять не нужно
🏁 Если коротко Партиционирование полностью управляется СУБД. Именно база знает: ✅ в какую партицию положить запись ✅ какие партиции читать при запросе ✅ какие индексы использовать Для приложения это по-прежнему одна таблица. А вся сложная логика скрыта внутри механизма партиционирования.