Перейти к содержанию

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


Шардированный кластер MongoDB распределяет данные по независимым шардам, чтобы масштабировать объём и пропускную способность записи/чтения сверх возможностей одного replica set. Критично спроектировать shard key: плохой ключ даёт hot shard и перекос нагрузки. Ниже — роли mongos, config servers, шарды, типы ключа (ranged / hashed), антипаттерны и короткие команды для лаборатории.


Когда шардировать

Ситуация Комментарий
Рост данных и I/O Один replica set упирается в диск/RAM/лидер записи
Изоляция нагрузок Разные диапазоны ключей можно косвенно разнести по шардам
Цена сложности Больше узлов, мониторинг, бэкапы, миграции chunk — только когда есть измеренные причины

Best practice: сначала индексы, схема, верный replica set и профиль запросов; шардирование — не замена оптимизации на одном кластере.


Архитектура sharded cluster

mongos (router)

  • Точка входа для приложения: маршрутизирует операции к нужным шардам по метаданным из config servers.
  • Можно поднимать несколько mongos за балансировщиком для отказоустойчивости и масштабирования маршрутизации.
  • Production: не подключайтесь к отдельным mongod шардов для обычных запросов — только через mongos (или явно осознанные исключения).

Config servers (реплика из трёх членов)

  • Хранят метаданные кластера: какие chunks к каким шардам относятся, настройки коллекций, версии конфигурации.
  • Потеря кворума config servers — серьёзный инцидент; планируйте бэкапы и восстановление по гайдам MongoDB.

Шарды (обычно — replica set на шард)

  • Каждый chunk данных физически живёт на одном шарде; balancer переносит chunks между шардами при перекосе.
  • Production: каждый шард = отдельный replica set, не одиночный mongod.

Схема (логически):

Приложение → mongos ─┬→ config servers (метаданные)
                     └→ shard A (RS) | shard B (RS) | …

Shard key: ranged vs hashed

Ключ задаётся при shardCollection и не меняется простой командой — смена ключа это тяжёлая операция (перенос данных). Проектирование «с первого раза» обязательно.

Ranged (префиксный / по диапазонам)

  • Диапазоны значений ключа отображаются на chunks по порядку.
  • Плюс: полезные targeted queries по префиксу ключа (могут идти на один/малый набор шардов).
  • Минус: если ключ монотонно растёт (например ObjectId по умолчанию на _id при вставках «в конец»), все новые документы попадают в один активный chunkhot shard на записи.
// Включить шардирование БД (один раз)
sh.enableSharding("shop")

// Ranged shard key по полю tenantId + orderId (пример)
sh.shardCollection("shop.orders", { tenantId: 1, orderId: 1 })

Hashed

  • MongoDB хэширует значение поля и раскладывает по chunks равномернее при однородном распределении исходного поля.
  • Плюс: сглаживает горячую точку для монотонных вставок, если единственный компонент ключа — хэшируемое поле.
  • Минус: точечные запросы без включения hashed-поля часто становятся scatter-gather по всем шардам.
sh.enableSharding("shop")

// Hashed по одному полю (частый шаблон для равномерной записи)
sh.shardCollection("shop.events", { ingestId: "hashed" })

// С compound hashed в новых версиях смотрите актуальную документацию — правила менялись по релизам

Best practice: выбирайте поля с высокой кардинальностью и равномерным распределением; избегайте ключей, где 80% записей попадают в мало значений.


Почему «плохой» shard key ломает кластер

Проблема Следствие
Низкая кардинальность Мало chunks, мало шардов реально задействовано
Hot spot (время Date.now() без variance, счётчик, монотонный _id на ranged) Один шард получает почти всю запись
Неравномерное распределение Balancer постоянно двигает данные; задержки, лишняя нагрузка
Запросы не содержат shard key Broadcast на все шарды — latency и нагрузка растут

Production: профилируйте распределение по значениям ключа до продакшена; согласуйте ключ с типичными фильтрами (find, агрегации).


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

// Обзор шардов, balancer, версия конфига
sh.status()

// Статистика по коллекции (chunks, shard, примеры размеров) — детали зависят от версии
db.orders.getShardDistribution()

// Куда уйдёт запись/чтение — отладка маршрутизации (mongosh)
db.orders.find({ tenantId: "t1" }).explain("executionStats")

Смотрите: количество chunks на шард, нет ли огромного перекоса, включён ли balancer, нет ли зависших миграций.


Практика: поднять sharded cluster

Минимальный учебный кластер (идея):

  • 1× config server replica set (в лаборатории исторически допускали упрощения; в production — полноценный replica set config).
  • Несколько шардов (хотя бы по одному процессу на шард в учебнике; в production — RS на шард).
  • Один или несколько mongos, указывающих на config DB.

Типичная последовательность после запуска всех процессов:

// На mongos: зарегистрировать шарды (хосты — ваши; формат см. документацию)
sh.addShard("shard1/rs-shard1-1:27017,rs-shard1-2:27017")
sh.addShard("shard2/rs-shard2-1:27017,rs-shard2-2:27017")

sh.enableSharding("labdb")
sh.shardCollection("labdb.metrics", { sensorId: "hashed" })

// Наполнить тестовыми документами и снова проверить
sh.status()
db.metrics.getShardDistribution()

Точные команды и порты зависят от docker-compose / Helm / cloud — ориентируйтесь на официальные туториалы по развёртыванию.


Production checklist (sharding)

# Практика
1 Shard key измерять на репрезентативных данных; избегать hot spots
2 Каждый шард — replica set; mongos и config servers с мониторингом и резервным копированием
3 Наблюдать chunk migration, balancer, задержки; алерты на перекос
4 Миграции схемы и zone (гео/tenant affinity) — заранее по Zones
5 Тестировать восстановление config + шардов в staging

Дополнительные материалы