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

Зачем нужно артиционирование в БД?

2.0 Middle🔥 101 комментариев
#Базы данных (SQL)

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

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

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

Партиционирование (Partitioning) в базах данных

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

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

ДО партиционирования:
┌─────────────────────────────────────┐
│        orders (10 млн строк)        │
│  Один огромный файл на диске        │
│  Медленные запросы и сканирования   │
└─────────────────────────────────────┘

ПОСЛЕ партиционирования:
┌──────────────┬──────────────┬──────────────┐
│  orders_2023 │  orders_2024 │  orders_2025 │
│  (3 млн)     │  (4 млн)     │  (3 млн)     │
│  Быстрые запросы, параллельные скан │
└──────────────┴──────────────┴──────────────┘

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

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

Разделение по диапазону значений (часто по датам).

CREATE TABLE orders (
    id SERIAL,
    user_id INT,
    amount DECIMAL,
    created_at TIMESTAMP,
    PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (YEAR(created_at)) (
    PARTITION p_2022 VALUES LESS THAN (2023),
    PARTITION p_2023 VALUES LESS THAN (2024),
    PARTITION p_2024 VALUES LESS THAN (2025),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);

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

Пример запроса (очень быстро):

-- Обращается ТОЛЬКО к p_2024 (1 млн строк вместо 10 млн)
SELECT * FROM orders WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';

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

Распределение равномерно по хэш-функции.

CREATE TABLE user_sessions (
    id SERIAL,
    user_id INT,
    session_data JSON,
    PRIMARY KEY (id, user_id)
) PARTITION BY HASH (user_id) PARTITIONS 16;

Как работает:

user_id=100 → hash(100) % 16 = 4 → partition 4
user_id=200 → hash(200) % 16 = 8 → partition 8
user_id=500 → hash(500) % 16 = 4 → partition 4

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

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

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

CREATE TABLE customer_orders (
    id SERIAL,
    customer_id INT,
    region VARCHAR(50),
    amount DECIMAL
) PARTITION BY LIST (region) (
    PARTITION p_us VALUES IN ('NY', 'CA', 'TX'),
    PARTITION p_eu VALUES IN ('UK', 'DE', 'FR'),
    PARTITION p_asia VALUES IN ('JP', 'CN', 'IN'),
    PARTITION p_other VALUES IN (DEFAULT)
);

Когда использовать: данные по географическому распределению, категориям.

4. Composite Partitioning (комбинированное)

Разделение по нескольким критериям.

CREATE TABLE events (
    id SERIAL,
    user_id INT,
    event_type VARCHAR(50),
    created_at TIMESTAMP
) PARTITION BY RANGE (YEAR(created_at))
  SUBPARTITION BY HASH (user_id) SUBPARTITIONS 8 (
    PARTITION p_2024 VALUES LESS THAN (2025),
    PARTITION p_2025 VALUES LESS THAN (2026)
);

Преимущества партиционирования

1. Улучшение производительности запросов

-- БЕЗ партиционирования: сканирует 10 млн строк
SELECT COUNT(*) FROM orders WHERE created_at >= '2024-01-01';

-- С партиционированием: сканирует только 2 млн строк (p_2024)
-- Скорость: 5x быстрее

Пример из production:

# Django ORM
orders = Order.objects.filter(
    created_at__gte='2024-01-01',
    created_at__lt='2025-01-01'
).count()
# БД сразу понимает: только p_2024

2. Более быстрые DELETE операции

-- БЕЗ партиционирования: удаляет 1 млн старых записей
DELETE FROM orders WHERE created_at < '2020-01-01';

-- С партиционированием: просто DROP PARTITION p_2019 (мгновенно!)
ALTER TABLE orders DROP PARTITION p_2019;

3. Параллельные операции

-- PostgreSQL может сканировать несколько партиций параллельно
-- EXPLAIN показывает: "Parallel Seq Scan"
SELECT * FROM orders WHERE created_at >= '2024-01-01';

4. Более простое управление индексами

-- Можно создать индексы только для активных партиций
CREATE INDEX idx_orders_2024 ON orders_2024(user_id);

-- Старые партиции могут быть архивированы

5. Лучше для больших таблиц

Таблица без партиций:  10 млн строк × 100 байт = ~1 ГБ
Индексы могут быть больше таблицы!

Таблица с партициями:  каждая партиция ~2 млн строк
Индексы меньше, работают быстрее

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

1. Усложнение запросов

-- Нужно помнить о partition key
-- ДА
SELECT * FROM orders WHERE created_at >= '2024-01-01';

-- НЕТ (полное сканирование всех партиций!)
SELECT * FROM orders WHERE user_id = 123;

2. Слабые места в JOIN

-- Если JOIN не по partition key, может быть медленно
SELECT o.*, u.name
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.created_at >= '2024-01-01';
-- Может потребовать все партиции!

3. Maintenance overhead

-- Нужно регулярно создавать новые партиции
-- Или использовать RANGE INTERVAL для автосоздания

CREATE TABLE orders (...)
PARTITION BY RANGE (INTERVAL '1 month' FOR created_at);

Практический пример: таблица логов

CREATE TABLE application_logs (
    id BIGSERIAL,
    user_id INT,
    log_level VARCHAR(10),  -- DEBUG, INFO, WARNING, ERROR
    message TEXT,
    created_at TIMESTAMP,
    PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (YEAR(created_at)) (
    PARTITION p_2023 VALUES LESS THAN (2024),
    PARTITION p_2024 VALUES LESS THAN (2025),
    PARTITION p_2025 VALUES LESS THAN (2026),
    PARTITION p_future VALUES LESS THAN MAXVALUE
);

-- Индекс по log_level на активной партиции
CREATE INDEX idx_logs_2025_level ON application_logs_p_2025(log_level);

-- Быстрый запрос: только в p_2025
SELECT * FROM application_logs 
WHERE created_at >= '2025-01-01' 
  AND log_level = 'ERROR';

Когда использовать партиционирование

  • Таблица > 1 ГБ (обычно 10+ млн строк)
  • Часто удаляются старые данные (достаточно DROP PARTITION)
  • Есть natural partition key (дата, регион, category)
  • Запросы фильтруют по partition key
  • Нужна архивизация данных

Когда НЕ использовать

  • Маленькие таблицы (< 100 млн строк)
  • Нет хорошего partition key
  • Запросы редко используют filter по partition key
  • Сложность не стоит выигрыша в производительности

Отличие от шардирования

Партиционирование (Partitioning):
- Одна БД, несколько партиций
- Прозрачно для приложения
- Автоматическое управление

Шардирование (Sharding):
- Несколько БД на разных серверах
- Приложение знает про шарды
- Ручное управление

Production пример: метрики системы

# Django модель с партиционированием
class SystemMetric(models.Model):
    server_id = models.IntegerField()
    cpu_usage = models.FloatField()
    memory_usage = models.FloatField()
    timestamp = models.DateTimeField()
    
    class Meta:
        db_table = 'system_metrics'
        # Для MySQL/PostgreSQL нужно SQL
        # CREATE TABLE system_metrics (...)
        # PARTITION BY RANGE INTERVAL ...

# Быстрый запрос (только за последний день)
metrics = SystemMetric.objects.filter(
    timestamp__gte=datetime.now() - timedelta(days=1)
).values('server_id').annotate(avg_cpu=Avg('cpu_usage'))

# БД сканирует только партицию за последний день!

Автоматическое управление партициями

-- PostgreSQL 11+ может автоматически создавать партиции
CREATE TABLE events (
    id SERIAL,
    event_data JSON,
    created_at TIMESTAMP
) PARTITION BY RANGE (created_at) (
    PARTITION p_2024_01 FOR VALUES FROM ('2024-01-01') TO ('2024-02-01'),
    PARTITION p_2024_02 FOR VALUES FROM ('2024-02-01') TO ('2024-03-01')
);

-- Или использовать pg_partman для автоматизации
CREATE TABLE events_partman (...)
  PARTITION BY RANGE (created_at);

-- pg_partman автоматически создает новые партиции

Мониторинг партиций

-- PostgreSQL: размер каждой партиции
SELECT 
    schemaname,
    tablename,
    pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as size
FROM pg_tables
WHERE tablename LIKE 'orders%'
ORDER BY size DESC;

-- MySQL: информация о партициях
SELECT 
    PARTITION_NAME,
    PARTITION_EXPRESSION,
    PARTITION_METHOD,
    PARTITION_ORDINAL_POSITION
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_NAME = 'orders' AND TABLE_SCHEMA = 'mydb';

Итог

Партиционирование — это критичная оптимизация для высоконагруженных систем:

  • Огромные таблицы (100+ млн строк) → почти обязательно
  • Частое удаление старых данных → ОЧЕНЬ полезно
  • Есть естественный ключ партиционирования → используй!

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

  • Таблиц логов (по дате)
  • Таблиц событий (по дате + hash)
  • Таблиц истории (по дате)
  • Аналитических данных (по географии)

Это дает 5-10x прирост производительности запросов на старые данные и делает удаление архивных данных тривиальным.