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

Где уместнее использовать шардирование БД?

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

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

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

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

Где уместнее использовать шардирование БД

Шардирование (sharding) — это техника горизонтального масштабирования базы данных. Это сложная тема, поэтому разберу её с учётом реальных сценариев и trade-off.

Что такое шардирование

Шардирование — это разделение данных таблицы по нескольким физическим базам данных (или серверам) так, чтобы каждый сервер хранил только подмножество данных.

Без шардирования:
┌─────────────────────────────────┐
│     PostgreSQL (1 сервер)       │
│  users: 1 млн записей           │
│  orders: 10 млн записей         │
└─────────────────────────────────┘

С шардированием (по user_id):
┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐
│  Shard 1         │  │  Shard 2         │  │  Shard 3         │
│  users 1-333k    │  │  users 334k-666k │  │  users 667k-1M   │
│  orders (их)     │  │  orders (их)     │  │  orders (их)     │
└──────────────────┘  └──────────────────┘  └──────────────────┘

Стратегии шардирования

1. Range-based (диапазонное)

def get_shard_id(user_id):
    # Распределяем по диапазонам
    if user_id < 334_000:
        return 1
    elif user_id < 667_000:
        return 2
    else:
        return 3

Минусы: неравномерное распределение нагрузки (hot spot)

2. Hash-based (хеширование)

def get_shard_id(user_id, num_shards=3):
    return hash(user_id) % num_shards

Плюс: равномерное распределение. Минус: перешардирование при добавлении шардов

3. Directory-based (справочник)

# Отдельная таблица mapping
# user_id → shard_id
shard_id = shard_directory.lookup(user_id)

Плюс: гибкость. Минус: extra hop для каждого запроса

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

ИСПОЛЬЗУЙ ШАРДИРОВАНИЕ, ЕСЛИ:

1. Размер данных превышает возможности одного сервера

Реальные примеры:
- Twitter: миллиарды твитов
- Facebook: петабайты пользовательских данных
- Uber: миллионы поездок в день

Примерно: >100 ГБ активных данных, растущих быстро

2. Нагрузка на один сервер неприемлема

# Пример нагрузки
requests_per_second = 50_000
db_connections_per_request = 1
total_connections_needed = requests_per_second * db_connections_per_request

# PostgreSQL обычно держит max 200-500 соединений комфортно
# Если нужно 50k коннектов — нужно шардирование

3. Необходимо горизонтальное масштабирование

Вертикальное масштабирование имеет потолок:
- Самый мощный сервер имеет конечные ресурсы
- Очень дорого
- Требует простоя

Шардирование = горизонтальное масштабирование
- Добавляем новые серверы
- Масштабируется линейно

НЕ ИСПОЛЬЗУЙ ШАРДИРОВАНИЕ, ЕСЛИ:

1. Данные легко помещаются на один сервер

Хорошие кандидаты на single database:
- Стартап < 10 млн записей
- SaaS внутри компании
- B2B сервис с ограниченным числом клиентов
- Проект < 1 млн DAU

2. Большинство запросов требуют данных из всех шардов

# Плохо для шардирования:
SELECT COUNT(*) FROM orders;  # Нужно запросить все 3 шарда
SELECT * FROM orders WHERE created_at > '2024-01-01';  # Span query

# Хорошо для шардирования:
SELECT * FROM orders WHERE user_id = 123;  # Один шард
SELECT * FROM users WHERE id = 456;  # Один шард

3. Требуются JOIN'ы между таблицами на разных шардах

# Проблема: user_id из shard 1, order_id из shard 3
SELECT u.name, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.id = 123 AND o.id = 456;

# JOIN работает, только если оба на одном шарде
# Иначе нужна app-level логика

Альтернативы шардированию

Перед тем, как выбрать шардирование, рассмотри:

1. Вертикальное масштабирование

- Больше CPU/RAM для сервера
- Проще в управлении
- Дешевле на начальном этапе

2. Репликация (Replication)

┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│  Primary DB  │───▶│  Replica 1   │───▶│  Replica 2   │
│  (write)     │    │  (read)      │    │  (read)      │
└──────────────┘    └──────────────┘    └──────────────┘

Масштабируем только читает, пишет в Primary

3. Кеширование (Caching)

# Redis cache
@cache.cached(timeout=3600)
def get_user(user_id):
    return db.query(User).filter(User.id == user_id).first()

Снижает нагрузку на БД на 70-90% в типичных сценариях.

4. Денормализация и документные БД

# Вместо JOIN'ов - денормализованные документы
# MongoDB: user_id + user_data + orders в одном документе
{
  "user_id": 123,
  "name": "Alice",
  "orders": [
    {"id": 1, "amount": 100},
    {"id": 2, "amount": 200}
  ]
}

Практический пример: Uber-like сервис

# Uber шардирует по city_id + vehicle_id
# Потому что:
# 1. Размер данных огромный (млн поездок в день)
# 2. Большинство запросов: "покажи поездки в моём городе"
# 3. Горячие данные (active rides) относительно небольшие

def get_shard(city_id, vehicle_type):
    shards_per_city = {
        'moscow': [1, 2, 3, 4],
        'spb': [5, 6, 7, 8],
        'nyc': [9, 10, 11, 12],
    }
    city_shards = shards_per_city[city_id]
    return city_shards[hash(vehicle_type) % len(city_shards)]

# Большинство запросов:
# SELECT * FROM rides WHERE city_id = 'moscow' AND vehicle_id = 456
# -> Обращаемся к одному шарду

Инструменты для шардирования

  • Citus (PostgreSQL) — встроенное шардирование в PostgreSQL
  • Vitess (MySQL) — middleware для шардирования MySQL
  • Django-sharding — для приложений на Django
  • MongoDB — встроенное шардирование

Вывод: процесс принятия решения

  1. Стартуем с single database — достаточно для 99% проектов
  2. Добавляем репликацию — когда нагрузка читает растёт
  3. Добавляем кеш — Redis/Memcached для горячих данных
  4. Шардируем — только если предыдущие меры исчерпаны

Шардирование — это последний шаг в масштабировании, потому что оно добавляет огромную сложность в операционном плане.

Где уместнее использовать шардирование БД? | PrepBro