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

В чем разница между шардированием и партиционированием БД?

1.8 Middle🔥 111 комментариев
#Архитектура и паттерны#Базы данных (SQL)

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

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

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

Шардирование vs Партиционирование: масштабирование БД

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

Партиционирование (Partitioning)

Партиционирование — это разделение одной таблицы на несколько частей (партиций) в пределах одной БД или одного сервера. Данные разделяются горизонтально по правилу (ключ партиции).

Характеристики:

  • Одна БД — всё находится на одном сервере
  • Горизонтальное разделение — строки разбиваются на группы
  • Партиция выбирается по ключу — обычно по диапазону дат, ID, региону
  • Прозрачна для приложения — автоматическое распределение
  • Улучшает производительность — быстрее поиск, меньше data lock
  • Упрощает обслуживание — легче архивировать старые данные
# Пример с PostgreSQL: партиционирование по дате
CREATE TABLE orders (
    id BIGINT PRIMARY KEY,
    user_id INT,
    order_date DATE,
    amount DECIMAL
) PARTITION BY RANGE (YEAR(order_date));

CREATE TABLE orders_2023 PARTITION OF orders
    FOR VALUES FROM ('2023-01-01') TO ('2024-01-01');

CREATE TABLE orders_2024 PARTITION OF orders
    FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');

# Для приложения это одна таблица
SELECT * FROM orders WHERE order_date BETWEEN '2024-01-01' AND '2024-12-31';
# БД автоматически ищет только в orders_2024
# На Python это выглядит как обычная работа с таблицей
from sqlalchemy import create_engine, Column, Integer, Date
from sqlalchemy.orm import Session

engine = create_engine('postgresql://user:pass@localhost/mydb')

with Session(engine) as session:
    # Для нас это одна таблица, БД сама управляет партициями
    orders = session.query(Order).filter(
        Order.order_date >= '2024-01-01'
    ).all()

Шардирование (Sharding)

Шардирование — это разделение данных между несколькими БД на разных серверах. Каждый шард (фрагмент) содержит подмножество данных и расположен на отдельном сервере.

Характеристики:

  • Несколько БД/серверов — горизонтальное масштабирование
  • Независимые шарды — каждый шард может быть отдельным сервером
  • Выбор шарда по ключу — обычно hash(user_id) % num_shards
  • Требует логика в приложении — нужно знать, какой шард использовать
  • Максимальная масштабируемость — можно добавлять серверы
  • Сложнее операции — join между шардами требует специальной логики
# Пример шардирования по user_id
# Шард 0: users 0-999999
# Шард 1: users 1000000-1999999
# Шард 2: users 2000000-2999999

class ShardManager:
    def __init__(self, num_shards=3):
        self.num_shards = num_shards
        self.connections = {}
        for i in range(num_shards):
            db_host = f'db-shard-{i}.example.com'
            self.connections[i] = self.connect(db_host)
    
    def get_shard_id(self, user_id):
        """Определяем, в какой шард пойдут данные"""
        return user_id % self.num_shards
    
    def save_user_order(self, user_id, order_data):
        """Сохраняем заказ в правильный шард"""
        shard_id = self.get_shard_id(user_id)
        connection = self.connections[shard_id]
        # Сохраняем в конкретный шард
        return connection.execute(
            "INSERT INTO orders (user_id, data) VALUES (%s, %s)",
            (user_id, order_data)
        )
    
    def get_user_orders(self, user_id):
        """Получаем заказы из правильного шарда"""
        shard_id = self.get_shard_id(user_id)
        connection = self.connections[shard_id]
        return connection.query(
            "SELECT * FROM orders WHERE user_id = %s",
            (user_id,)
        )

# Использование
manager = ShardManager(num_shards=3)
manager.save_user_order(user_id=123, order_data={...})
orders = manager.get_user_orders(user_id=123)

Сравнение в таблице

АспектПартиционированиеШардирование
РазмещениеОдна БДНесколько БД/серверов
СерверОдинМного
УправлениеАвтоматическое (БД)Ручное (приложение)
Сложность приложенияПростаяСложная
МасштабируемостьОграничена серверомНеограниченна
Join между разделамиВозможныОчень сложны
Переход между шардамиN/AТребует миграции данных
АдминистрированиеПрощеСложнее
Когда использовать< 100GB данных> 1TB данных

Визуальное сравнение

Партиционирование (одна таблица на одном сервере)

┌─────────────────────────────────────────┐
│         PostgreSQL сервер               │
│  ┌────────────────────────────────────┐ │
│  │       Таблица: orders              │ │
│  │  ┌──────────────────────────────┐  │ │
│  │  │ Partition 2023 (Jan-Dec)     │  │ │
│  │  │ 1M строк                     │  │ │
│  │  └──────────────────────────────┘  │ │
│  │  ┌──────────────────────────────┐  │ │
│  │  │ Partition 2024 (Jan-Dec)     │  │ │
│  │  │ 2M строк                     │  │ │
│  │  └──────────────────────────────┘  │ │
│  │  ┌──────────────────────────────┐  │ │
│  │  │ Partition 2025 (Jan-Dec)     │  │ │
│  │  │ 500K строк                   │  │ │
│  │  └──────────────────────────────┘  │ │
│  └────────────────────────────────────┘ │
└─────────────────────────────────────────┘

Шардирование (таблица на нескольких серверах)

Приложение
    |
    v
┌───────────────────────────────────────┐
│       Shard Manager/Router            │
│  (hash(user_id) % 3 -> shard_id)      │
└────────┬────────────────┬─────────────┘
         |                |
    ┌────v────┐      ┌────v────┐      ┌─────────┐
    │ Shard 0 │      │ Shard 1 │      │ Shard 2 │
    │ Server A│      │ Server B│      │ Server C│
    ├─────────┤      ├─────────┤      ├─────────┤
    │users    │      │users    │      │users    │
    │0-999999 │      │1M-1.9M  │      │2M-2.9M  │
    │1M rows  │      │1M rows  │      │1M rows  │
    └─────────┘      └─────────┘      └─────────┘

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

Начинаем с партиционирования

# Этап 1: небольшой сервис с одной БД
# 50GB данных — партиционируем по датам

CREATE TABLE events (
    id BIGINT,
    user_id INT,
    event_date DATE,
    data JSONB
) PARTITION BY RANGE (event_date);

# Приложение работает как обычно
events = db.query(Event).filter(Event.event_date >= '2024-01-01').all()

Перехождим на шардирование

# Этап 2: растём и нужно масштабировать дальше
# 500GB+ данных — шардируем по user_id

from consistent_hash import ConsistentHashRing

class EventShardManager:
    def __init__(self):
        # 5 шардов для примера
        self.ring = ConsistentHashRing(
            nodes=[f'shard-{i}' for i in range(5)]
        )
        self.connections = {
            f'shard-{i}': connect(f'db-shard-{i}.example.com')
            for i in range(5)
        }
    
    def log_event(self, user_id, event_type, data):
        shard_key = self.ring.get_node(str(user_id))
        connection = self.connections[shard_key]
        connection.execute(
            "INSERT INTO events (user_id, type, data) VALUES (%s, %s, %s)",
            (user_id, event_type, data)
        )
    
    def get_user_events(self, user_id):
        shard_key = self.ring.get_node(str(user_id))
        connection = self.connections[shard_key]
        return connection.query(
            "SELECT * FROM events WHERE user_id = %s",
            (user_id,)
        )

# Использование
manager = EventShardManager()
manager.log_event(user_id=12345, event_type='click', data={...})

Проблемы шардирования

Горячий шард (Hot Shard)

# Если один пользователь создаёт 99% всех событий
# его шард перегружается

def get_shard_id(user_id):
    return user_id % 3  # Простой hash

# Пользователь 1 -> Shard 1 (горячий)
# Пользователь 2 -> Shard 2
# Пользователь 3 -> Shard 0

# Решение: используй consistent hashing
from hashlib import md5
def get_shard_id(user_id):
    return int(md5(str(user_id).encode()).hexdigest(), 16) % 3

Cross-shard query

# Нельзя просто написать JOIN между шардами

# НЕПРАВИЛЬНО:
SELECT * FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2024-01-01';

# ПРАВИЛЬНО: запросить из каждого шарда отдельно
def get_all_recent_orders():
    all_orders = []
    for shard in self.shards:
        orders = shard.query(
            "SELECT * FROM orders WHERE created_at > %s",
            ('2024-01-01',)
        )
        all_orders.extend(orders)
    return all_orders

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

Партиционирование — если:

  • Таблица слишком большая — миллиарды строк в одной таблице
  • Операции по диапазонам — часто ищем по датам, регионам
  • Один сервер может справиться — объем данных < 10TB
  • Нужна простота — приложение не должно знать о разделении

Шардирование — если:

  • Данные больше одного сервера — > 10TB
  • Нужна горизонтальная масштабируемость — добавлять серверы
  • Готовы к сложности — приложение управляет маршрутизацией
  • Есть natural shard key — user_id, tenant_id, etc.

Заключение

Партиционирование — это оптимизация одной таблицы на одном сервере, управляется БД автоматически. Шардирование — это распределение данных между несколькими серверами, требует управления на уровне приложения. Начинайте с партиционирования, когда одного сервера недостаточно — переходите на шардирование. Оба подхода могут использоваться вместе (партиции внутри каждого шарда).

В чем разница между шардированием и партиционированием БД? | PrepBro