← Назад к вопросам

Что такое партиционирование?

2.0 Middle🔥 101 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Партиционирование (Partitioning): Масштабирование баз данных

Партиционирование — это техника разделения большой таблицы на несколько меньших физических таблиц, которые хранятся отдельно, но логически представляют одну таблицу. Это критическая техника для масштабирования баз данных.

Основная идея

Есть таблица с 100 миллионами записей. Вместо того, чтобы искать в одной большой таблице:

Одна большая таблица (медленно):
[запись 1][запись 2][запись 3]...[запись 100M]
       ↑
       Поиск требует сканирования всей таблицы

Партиционированная таблица (быстро):
Партиция 1: [1-25M]    <- Поиск только в нужной партиции
Партиция 2: [25-50M]
Партиция 3: [50-75M]
Партиция 4: [75-100M]

Три типа партиционирования

1. Range Partitioning (по диапазону)

Делит данные по значению столбца в диапазоны.

-- Таблица заказов, партиционирована по дате
CREATE TABLE orders (
    order_id INT,
    customer_id INT,
    order_date DATE,
    amount DECIMAL
) PARTITION BY RANGE (YEAR(order_date)) (
    PARTITION p2020 VALUES LESS THAN (2021),
    PARTITION p2021 VALUES LESS THAN (2022),
    PARTITION p2022 VALUES LESS THAN (2023),
    PARTITION p2023 VALUES LESS THAN (2024),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);

-- Запрос автоматически ищет только в p2023
SELECT * FROM orders WHERE order_date = '2023-05-15';
-- База знает: нужна только партиция p2023

Когда использовать: Временные данные, логи, события по датам

Плюсы:

  • Быстрое удаление старых данных (drop partition вместо delete)
  • Архивирование по периодам
  • Очень быстрые range queries
-- Быстро удалить все данные за 2020
ALTER TABLE orders DROP PARTITION p2020;
-- Вместо: DELETE FROM orders WHERE YEAR(order_date) = 2020; (медленно!)

2. Hash Partitioning (по хешу)

Делит данные по хешу столбца равномерно.

-- Таблица пользователей, 4 партиции по хешу user_id
CREATE TABLE users (
    user_id INT,
    username VARCHAR(100),
    email VARCHAR(100)
) PARTITION BY HASH(user_id)
PARTITIONS 4;

-- user_id 1001 -> hash(1001) % 4 = 1 -> партиция 1
-- user_id 2002 -> hash(2002) % 4 = 2 -> партиция 2
-- Данные распределены равномерно

SELECT * FROM users WHERE user_id = 1001;  -- Быстро
SELECT * FROM users WHERE username = 'john';  -- Медленно (нужно сканировать все партиции)

Когда использовать: Когда нужно равномерное распределение данных

Плюсы:

  • Равномерное распределение
  • Простая балансировка

Минусы:

  • Нельзя эффективно делать range queries
  • Сложнее переоформировать партиции

3. List Partitioning (по спискам значений)

Делит данные по конкретным значениям.

-- Таблица продаж по регионам
CREATE TABLE sales (
    sale_id INT,
    region VARCHAR(50),
    amount DECIMAL
) PARTITION BY LIST(region) (
    PARTITION p_eu VALUES IN ('UK', 'France', 'Germany'),
    PARTITION p_asia VALUES IN ('Japan', 'China', 'India'),
    PARTITION p_americas VALUES IN ('USA', 'Canada', 'Brazil'),
    PARTITION p_other VALUES IN (DEFAULT)
);

-- Запрос для EU быстро найдет нужную партицию
SELECT * FROM sales WHERE region = 'Germany';

Когда использовать: Категориальные данные (регионы, страны, типы)

Разница: Partitioning vs Sharding

Это часто путают, но это разные концепции.

Partitioning (внутри одного сервера)

Повых оптимизаций
Один сервер:
┌─────────────────────────┐
│ Одна база данных        │
│ ┌────────┬────────┬────┐ │
│ │Part 1  │Part 2  │Part 3│ │
│ └────────┴────────┴────┘ │
└─────────────────────────┘

Нужен для: Оптимизации больших таблиц внутри одного сервера

Sharding (распределение по разным серверам)

Несколько серверов:
Сервер 1              Сервер 2              Сервер 3
┌──────────────┐      ┌──────────────┐      ┌──────────────┐
│Shard 1       │      │Shard 2       │      │Shard 3       │
│user_id 1-3M  │      │user_id 3-6M  │      │user_id 6-10M │
└──────────────┘      └──────────────┘      └──────────────┘

Нужен для: Горизонтального масштабирования между серверами

Практический пример: Логирование событий

-- Большая таблица логов: 1 млрд записей в день
CREATE TABLE events (
    event_id BIGINT,
    user_id INT,
    event_type VARCHAR(50),
    created_at TIMESTAMP
) PARTITION BY RANGE (DATE(created_at)) (
    PARTITION p_2024_01_01 VALUES LESS THAN ('2024-01-02'),
    PARTITION p_2024_01_02 VALUES LESS THAN ('2024-01-03'),
    PARTITION p_2024_01_03 VALUES LESS THAN ('2024-01-04'),
    -- ... и так далее
    PARTITION p_future VALUES LESS THAN MAXVALUE
);

-- Запрос за сегодня — быстро (только одна партиция)
SELECT COUNT(*) FROM events WHERE DATE(created_at) = '2024-01-03';
-- Сканирует только p_2024_01_03

-- Удалить логи старше месяца — очень быстро
ALTER TABLE events DROP PARTITION p_2024_01_01;
-- Удаляет целую партицию, не трогая остальное

Индексы на партиционированных таблицах

-- Локальный индекс (по одному на каждую партицию)
CREATE INDEX idx_user_id 
ON events (user_id)
LOCAL;  -- Индекс распределен по партициям

-- Глобальный индекс (один на все партиции)
CREATE INDEX idx_event_type 
ON events (event_type)
GLOBAL;  -- Один индекс на все партиции

Производительность: До и После

До партиционирования

# Одна таблица с 100M записей
# Запрос: SELECT * FROM orders WHERE order_date = '2023-05-15'

# Выполнение:
# 1. Index seeks на order_date
# 2. Сканирует ~100K записей из индекса
# 3. Fetches данные из таблицы
# Time: ~500ms

После партиционирования

# Таблица разбита по году: 12 партиций
# Запрос: SELECT * FROM orders WHERE order_date = '2023-05-15'

# Выполнение:
# 1. Partition pruning: база знает, что нужна только p2023
# 2. Index seeks на order_date в p2023 (~8M записей вместо 100M)
# 3. Сканирует ~8K записей из индекса
# 4. Fetches данные из таблицы
# Time: ~50ms  <- 10x быстрее!

Недостатки и сложности

# 1. Сложность операций
join_result = session.query(Orders).join(OrderDetails).filter(
    Orders.order_date > '2023-01-01'
).all()
# Если таблицы партиционированы по разным столбцам — может быть медленно

# 2. Перебалансирование
# Если добавили новые партиции — может потребоваться redistribution

# 3. Уникальные ключи
# Partition key должен быть частью primary key или unique constraint

Когда партиционировать?

Когда нужно:

  • Таблица > 100GB
  • Нужна частая очистка старых данных
  • Есть очевидный key для разделения (дата, region, user_id)
  • Запросы часто фильтруют по этому ключу

Когда НЕ нужно:

  • Таблица < 10GB
  • Много JOIN-ов между партиционированными таблицами
  • Нет четкого ключа для разделения
  • Нужны ACID транзакции между партициями

Python пример: Работа с партиционированной таблицей

from sqlalchemy import create_engine, Column, Integer, String, DateTime
from sqlalchemy.orm import declarative_base, Session
from datetime import datetime, timedelta

Base = declarative_base()

class Event(Base):
    __tablename__ = 'events'
    
    event_id = Column(Integer, primary_key=True)
    user_id = Column(Integer)
    event_type = Column(String)
    created_at = Column(DateTime)

engine = create_engine('postgresql://...')

# Автоматическое добавление новой партиции
def ensure_partition_for_date(engine, date):
    """Создает партицию если её нет."""
    partition_name = f"p_{date.strftime('%Y_%m_%d')}"
    next_date = date + timedelta(days=1)
    
    with engine.connect() as conn:
        conn.execute(f"""
            ALTER TABLE events 
            ADD PARTITION {partition_name} 
            VALUES LESS THAN ('{next_date.date()}');
        """)
        conn.commit()

# Использование
today = datetime.now().date()
ensure_partition_for_date(engine, today)

# Быстрый поиск в нужной партиции
with Session(engine) as session:
    today_events = session.query(Event).filter(
        Event.created_at >= datetime.combine(today, datetime.min.time()),
        Event.created_at < datetime.combine(today + timedelta(days=1), datetime.min.time())
    ).all()

Ключевые выводы

  1. Партиционирование разделяет большую таблицу на меньшие физические части
  2. Range partitioning для временных данных (логи, события)
  3. Hash partitioning для равномерного распределения
  4. List partitioning для категориальных данных
  5. Partition pruning позволяет базе сканировать только нужные партиции
  6. Не партиционируй пока таблица не станет > 100GB
  7. Главная выгода — быстрое удаление старых данных и улучшение performance на больших таблицах
Что такое партиционирование? | PrepBro