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

2. Архитектура: replica set и WiredTiger


Replica set — минимально осмысленная топология MongoDB в production: отказоустойчивость и основа для бэкапов и обновлений без длинного простоя. Отдельный mongod без репликации годится разве что для локальной разработки. Ниже — роли узлов, выборы лидера (election), write/read concern, движок WiredTiger и практика с тремя узлами.


Зачем replica set в production

Идея Комментарий
Высокая доступность При падении primary кластер сам выбирает нового лидера
Чтение с вторичных Распределение read-only нагрузки (read preference) — с оговорками по задержке репликации
Rolling updates Обновление версии MongoDB по узлу, пока кворум жив
Операционная дисциплина Бэкапы, точки восстановления — проектировать отдельно; реплика не заменяет бэкап

Один инстанс без репликации: любой сбой диска/хоста = простой и риск потери данных при неверном writeConcern.


Роли узлов: Primary, Secondary, Arbiter

Primary

  • Принимает записи (в типичной схеме приложение пишет только в primary).
  • Записи попадают в oplog; вторичные тянут oplog и применяют изменения.

Secondary

  • Копирует данные с primary (через oplog).
  • Может обслуживать чтения, если драйвер/запрос настроены на secondary / secondaryPreferred и это осознанно (см. stale reads).

Arbiter (голосует, данных не хранит)

  • Участвует только в голосовании за primary, чтобы набрать нечётное число голосов при двух data-узлах.
  • Production: по возможности избегайте «primary + secondary + arbiter» как единственной схемы: при падении одной data-ноды остаётся один носитель данных — нет избыточности копии. Предпочтительнее три data-узла (или 2 data + arbiter только как компромисс с пониманием рисков).

Election: что происходит при падении primary?

Кратко:

  1. Узлы перестают получать heartbeat от primary.
  2. Условия кворума и приоритетов проверяются; secondary инициирует выборы.
  3. Один узел становится новым primary (часто за несколько секунд, зависит от сети и настроек).
  4. Драйвер с SRV/seed list узнаёт нового primary и перенаправляет записи.

Вопрос уровня middle+: что будет с приложением?

Аспект Практика
Записи во время переключения Краткое окно: NotWritablePrimary / сетевые ошибки — драйверы часто повторяют запись после появления нового primary
Уже «подтверждённая» запись Зависит от writeConcern: w: "majority" + journal там, где нужна устойчивость к падению узла
Чтения с secondary Возможны устаревшие данные до догона вторичной за новым oplog
Split-brain / partition Настройки кворума и сеть между узлами критичны; «один узел думает, что он primary» в изолированной партиции — отдельная тема (см. документацию по election и priority)

Best practice: в connection string указывайте все члены replica set (или SRV), включите разумный retryWrites там, где поддерживается, и явно задайте writeConcern для доменно важных операций.

Пример: явный write concern при записи

// Вставка с ожиданием подтверждения на majority узлов (типично для критичных данных)
db.orders.insertOne(
  { orderId: "ORD-2001", total: 10 },
  { writeConcern: { w: "majority", wtimeout: 5000 } },
)

Read Concern и чтение с вторичных

Значение Смысл (упрощённо)
local Данные как видит узел локально; для secondary — могут быть ещё не реплицированы все записи primary
majority Чтение только подтверждённой majority записи (сильнее для согласованности, дороже по задержке)
available Оптимизация для шардов; для реплик детали см. документацию
// Пример: чтение с согласованностью majority (mongosh; в приложении — опции драйвера)
db.orders.find({ orderId: "ORD-2001" }).readConcern("majority")

Production: если читаете с secondary, документируйте допустимую рекламацию (lag) и используйте readConcern / readPreference согласно продуктовым требованиям.


Storage Engine: WiredTiger

Современный движок по умолчанию — WiredTiger.

Компонент Комментарий
Кэш Внутренний cache WT; на выделенном сервере часто ориентируются на ~50% RAM под WiredTiger (не жёсткое правило — смотреть метрики и конкуренцию с ОС page cache)
Сжатие Коллекции/индексы могут использовать snappy / zstd — экономия диска, небольшой CPU overhead
Журнал (journaling) Гарантия durability при некорректном выключении; в production journaling обычно включён

Best practice: не отключайте journaling на боевых узлах; подбирайте RAM и мониторьте использование cache, slow ops, cache pressure.


Практика: три узла replica set (локально)

Упрощённая схема — три mongod в одной сети Docker с общим ключом и одним именем replica set rs0.

1. Запуск трёх инстансов (идея)

На каждом узле: один и тот же --replSet rs0, разные порты/имена, общий keyFile для внутренней аутентификации членов (в production обязательно).

# Пример ключя (права 400) — только для лаборатории
openssl rand -base64 756 > rs-keyfile
chmod 400 rs-keyfile

Типичные флаги процесса (концептуально):

mongod --replSet rs0 --bind_ip_all --port 27017 --keyFile /path/to/rs-keyfile
# второй узел: порт 27018, третий: 27019 — на разных контейнерах/хостах

2. Инициализация и проверка

Подключитесь к одному узлу и выполните:

rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "mongo1:27017", priority: 2 },
    { _id: 1, host: "mongo2:27017", priority: 1 },
    { _id: 2, host: "mongo3:27017", priority: 1 },
  ],
})

// Статус: кто primary, кто secondary, здоровье
rs.status()

// Конфигурация (priority, votes, hidden и т.д.)
rs.conf()

3. Симуляция: падение primary

  • Остановите контейнер/процесс текущего primary (смотрите rs.status().membersstateStr: "PRIMARY").
  • Через несколько секунд другой узел станет PRIMARY; rs.status() покажет смену.
  • Запишите приложением в паузу — ожидайте краткие ошибки, затем восстановление после reconnect.

4. Симуляция: network partition

  • Изолируйте primary от остальных (iptables / отдельная Docker network без маршрута к majority).
  • Узел в меньшинстве не должен обслуживать записи как primary долго (поведение зависит от сценария); majority сторона выберет нового primary.
  • Production-урок: избегайте топологий, где один ЦОД = «ровно половина» голосов без продуманного Replica Set Across Datacenters.

Production checklist (replica set)

# Практика
1 Минимум три голосующих узла с пониманием, где живут данные (не только arbiter)
2 Явный writeConcern для критичных путей; readConcern / readPreference — по SLA
3 Мониторинг repl lag, elections, oplog window, CPU/RAM WiredTiger
4 TLS, аутентификация, keyFile или x509 между членами; секреты из vault
5 Регулярные бэкапы (filesystem snapshots / mongodump + policy RTO/RPO) и тест восстановления

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