Когда данных становится много, один сервер перестаёт справляться — медленно читает, медленно пишет, и если упадёт, приложение встанет. MongoDB решает это двумя механизмами: репликация хранит копии данных на нескольких серверах (надёжность), шардинг разрезает коллекцию между серверами (горизонтальное масштабирование). Разберём оба.
Почему нельзя просто держать один сервер
Представьте интернет-магазин с миллионами товаров. При одном сервере:
- Если сервер упадёт — сайт недоступен, продажи стоят.
- Если данных больше, чем помещается в оперативную память — каждый запрос идёт на диск, скорость падает в десятки раз.
- Нагрузка на запись ограничена скоростью одного диска.
Репликация решает первую проблему, шардинг — вторую и третью.
Replica set — резервные копии в реальном времени
Replica set — это группа серверов MongoDB, которые хранят одни и те же данные. Минимальная конфигурация — три узла: один primary принимает все записи, два secondary автоматически копируют изменения.
┌──────────┐
write → │ primary │ ──── репликация ────┐
└──────────┘ │
▼
┌──────────┐ ┌──────────┐
read ──────────────────────── │secondary │ │secondary │
└──────────┘ └──────────┘
Почему три, а не два? Потому что при сбое узлы голосуют, кто станет новым primary. Нужно большинство — при двух серверах одного голоса недостаточно. Три узла дают кворум.
Как работает oplog
Механизм репликации построен на oplog — специальной коллекции в базе local, куда primary записывает каждую успешную операцию. Secondary постоянно читают этот журнал и воспроизводят операции у себя.
Это похоже на бухгалтерскую книгу: primary записывает «добавлен товар X», «изменена цена Y», а secondary «переигрывают» эти действия. Если secondary отстал и снова подключился — он догоняет по oplog.
Один важный момент: oplog имеет фиксированный размер (по умолчанию около 5% свободного диска). Если secondary отстал сильно и нужных записей в oplog уже нет — придётся копировать весь массив данных заново (initial sync). Поэтому при большой нагрузке oplog стоит увеличивать.
Что происходит при падении primary
Secondaries обнаруживают пропажу primary через несколько секунд и начинают голосование. Побеждает тот, у кого самый свежий oplog. Вся процедура занимает 10–30 секунд — в это время кластер не принимает записи.
Важное правило: если из трёх узлов два недоступны — оставшийся один переходит в режим только для чтения. Это защита от split brain: если бы оба «выживших» сегмента продолжали принимать записи независимо, данные разошлись бы.
Куда идут запросы на чтение
По умолчанию все запросы идут на primary. Но можно направить чтение на secondary — чтобы разгрузить primary или читать из ближайшего географически узла. Это называется read preference:
| Режим | Откуда читаем | Когда использовать |
|---|---|---|
primary (по умолчанию) | Только primary | Когда нужны самые актуальные данные |
secondary | Только secondary | Аналитика, отчёты — не нагружать primary |
secondaryPreferred | Secondary, при недоступности — primary | Нагрузка на чтение, данные могут немного отставать |
nearest | Узел с минимальной задержкой | Распределённые кластеры в разных регионах |
Подводный камень: secondary может немного отставать от primary. Если вы только что записали данные и сразу читаете — используйте primary, иначе можете прочитать устаревшее.
db.product.find({ categoryId: 1 })
.readPref("secondaryPreferred");
Когда одной replica set уже мало
Replica set решает проблему надёжности, но не масштабирования. Если данных настолько много, что они не помещаются в память одного сервера — запросы начинают ходить на диск, и скорость падает в десятки раз.
Практический ориентир: если активный набор данных (те документы, к которым обращаются чаще всего) занимает больше 70% оперативной памяти — пора думать о следующем шаге.
Следующий шаг в MongoDB — sharded cluster: данные физически разрезаются между несколькими replica set.
Sharded cluster — как устроено изнутри
В шардированном кластере четыре типа компонентов:
client → mongos (роутер) → Shard A (replica set)
→ Shard B (replica set)
→ Shard C (replica set)
↕
Config Servers (replica set из 3 узлов)
- mongos — точка входа. Клиент подключается к mongos, не зная о шардах. Mongos смотрит в метаданные и направляет запрос на нужный шард. Mongos можно запустить несколько штук — он не хранит данные, работает как прокси.
- Config servers — три узла, которые хранят карту: какие данные на каком шарде. Без них кластер не работает.
- Shards — обычные replica set, каждый хранит свою часть данных.
- Balancer — фоновый процесс, который следит за равномерностью. Если один шард перегружен — перемещает часть данных на другой.
Данные внутри коллекции делятся на chunks (куски по 128 МБ). Balancer перемещает chunks между шардами, чтобы нагрузка была примерно одинаковой.
Важно понимать цену: минимальный production-кластер — это 3 узла config servers + 2 шарда по 3 реплики + 2 mongos = 11 серверов. Это значительно дороже одной replica set. Шардинг включают только когда без него действительно не обойтись.
Shard key — самое важное решение при шардинге
Shard key — поле (или несколько полей), по которому MongoDB решает, на какой шард положить документ. Это решение принимается один раз и менять его сложно — поэтому выбор shard key требует внимания.
Равномерное распределение против горячего шарда
Представьте: магазин шардирует коллекцию товаров по полю _id типа ObjectId. ObjectId монотонно растёт со временем — все новые товары попадают на один и тот же шард. Он перегружен, остальные простаивают. Это называется hot shard — главная ошибка при шардинге.
Ranged sharding — документы распределяются по диапазонам значений. Подходит когда значения равномерно распределены или когда запросы часто фильтруют по диапазону (например, по дате):
sh.shardCollection("shop.product", { categoryId: 1 });
Hashed sharding — MongoDB сама хеширует значение, распределение всегда равномерное. Хорошо для монотонных ключей вроде ObjectId или timestamp. Минус: запросы по диапазону вынуждены опрашивать все шарды:
sh.shardCollection("shop.product", { _id: "hashed" });
Составной shard key
Часто лучший вариант — составной ключ: первая часть даёт разнообразие (равномерность), вторая — локальность для типичных запросов.
Если запросы обычно идут по categoryId, а в каждой категории товаров разное количество:
sh.shardCollection("shop.product", { categoryId: 1, _id: "hashed" });
Товары одной категории хешируются по _id и распределяются по шардам равномерно. При этом запросы по categoryId идут на меньшее число шардов, чем при чистом хешировании по _id.
Zoned sharding — данные в нужном регионе
Если регуляторика требует хранить данные пользователей в определённой стране — используется zoned sharding: диапазонам значений shard key назначаются теги, шардам присваиваются те же теги:
sh.addShardTag("shardEU", "EU");
sh.addShardTag("shardUS", "US");
sh.addTagRange(
"shop.product",
{ region: "EU", productId: MinKey },
{ region: "EU", productId: MaxKey },
"EU"
);
Данные с region: "EU" физически останутся на европейских серверах.
Как выбрать хороший shard key
Пять критериев, которые нужно проверить:
- Высокая кардинальность — много разных значений. Поле
statusс тремя значениями не позволит распределить данные по десяти шардам. - Равномерное распределение — иначе горячий шард.
userIdобычно хорошо,countryдля одной страны — плохо. - Присутствует в большинстве запросов — иначе каждый запрос опрашивает все шарды (scatter-gather), что медленно.
- Редко меняется — shard key практически неизменный. Изменение с 5.0 возможно через
reshardCollection, но это долгая операция. - Запись идёт на один шард — каждая операция записи попадает на конкретный шард без распределённых транзакций.
Практический совет: если коллекция product шардируется, а коллекция category нет — запросы с $lookup между ними вынуждены опрашивать все шарды. Лучше шардировать обе коллекции по categoryId: тогда связанные документы окажутся физически рядом и соединение останется локальным.
Коротко
- Replica set — три и более узлов, хранящих одни данные. Primary принимает записи, secondary реплицируют через oplog.
- При падении primary узлы голосуют и выбирают нового — 10–30 секунд без записи. Это нормально.
- Запись с
w: "majority"переживёт failover; сw: 1— нет. - Oplog — журнал операций; если secondary отстал дальше его границы, нужен полный начальный sync.
- Чтение можно направить на secondary через
readPreference— разгрузить primary или читать из ближайшего узла. - Sharded cluster нужен когда активные данные не помещаются в память одного сервера. Минимум 11 узлов — дорого.
- Shard key — главное решение: высокая кардинальность, равномерность, присутствие в запросах, редкие изменения.
- Монотонный ключ без хеширования создаёт hot shard — все записи в один шард. Решение:
hashedшардинг. - Составной shard key часто лучше одного поля: равномерность + локальность запросов.
Что почитать дальше
- ACID и согласованность в MongoDB — какие гарантии работают в sharded cluster и как использовать causal consistency.
- Моделирование документов — правильная схема снижает нагрузку и откладывает потребность в шардинге.