Диагностика и устранение распространенных проблем с задержкой репликации MongoDB

Разберитесь в сложностях задержки репликации MongoDB с помощью этого подробного руководства. Узнайте, как выявлять, диагностировать и устранять распространенные проблемы, которые ставят под угрозу согласованность данных и высокую доступность в ваших наборах реплик. Статья охватывает все: от понимания oplog и обнаружения задержки с помощью `rs.status()` до практических решений для недостаточного размера oplog, узких мест сети, ограничений ресурсов и отсутствующих индексов. Вооружитесь действенными стратегиями и лучшими практиками для поддержания здоровой, производительной и отказоустойчивой среды MongoDB.

Диагностика и устранение распространенных проблем с задержкой репликации MongoDB

Задержка репликации MongoDB — это не просто цифра на панели мониторинга. Она меняет поведение вашего приложения. Пользователь обновляет профиль, другой запрос читает со вторичного узла, и возвращается старое значение. Узел выходит из строя, но лучший вторичный узел все еще отстает, поэтому отказоустойчивость занимает больше времени, чем ожидалось. Отчетный запрос попадает на неправильный узел, и внезапно набор реплик выглядит здоровым, за исключением одного вторичного узла, который постоянно отстает от первичного.

Полезный способ думать о задержке репликации прост: первичный узел создает записи oplog быстрее, чем один или несколько вторичных узлов могут их получить и применить. Исправление зависит от того, какая часть этого предложения верна в вашей среде. Иногда первичный узел пишет слишком много всплесками. Иногда вторичный узел недостаточно мощный. Иногда сеть медленная. Иногда задержка является преднамеренной, потому что узел настроен с secondaryDelaySecs. Ваша первая задача — разделить эти случаи, прежде чем вносить изменения.

Начните с фактической формы задержки

Не начинайте с изменения размера oplog или перезапуска mongod. Сначала выясните, является ли задержка постоянной, скачкообразной, ограниченной одним узлом или затрагивает все вторичные узлы.

В mongosh начните с:

rs.status()

Посмотрите на stateStr, optimeDate, lastHeartbeatMessage и поля health каждого узла. Если один вторичный узел отстает, а остальные актуальны, вероятно, у вас проблема, специфичная для узла: диск, ЦП, локальные чтения, локальное обслуживание или плохой сетевой путь. Если все вторичные узлы отстают, внимательнее посмотрите на объем записи первичного узла, пропускную способность сети от первичного узла или необычно большую операцию.

Для быстрой проверки окна oplog выполните:

rs.printReplicationInfo()

Окно oplog показывает, сколько времени покрывается текущим oplog. Это не говорит о том, что репликация здорова. Это говорит о том, как далеко может отстать вторичный узел, прежде чем ему потребуется начальная синхронизация. Если ваше окно oplog составляет 6 часов, а окна обслуживания обычно занимают 8 часов, у вас есть реальный операционный риск, даже если текущая задержка равна нулю.

Для вторичных узлов это также полезно:

rs.printSecondaryReplicationInfo()

В старых примерах вы можете увидеть rs.printSlaveReplicationInfo(). В более новых формулировках используется "secondary", но старые помощники оболочки и старые сообщения в блогах все еще могут использовать "slave". Поля важнее, чем название.

Если вы хотите небольшой скрипт для живой оболочки, сравните время первичного узла с каждым вторичным:

const status = rs.status();
const primary = status.members.find(m => m.stateStr === "PRIMARY");

status.members
  .filter(m => m.stateStr === "SECONDARY")
  .forEach(m => {
    const lagSeconds = (primary.optimeDate - m.optimeDate) / 1000;
    print(`${m.name}: ${lagSeconds}s behind primary`);
  });

Относитесь к этому как к снимку, а не как к диагнозу. Вторичный узел, который отстает на 20 секунд во время пакетного импорта, может быть в порядке, если он быстро догоняет. Вторичный узел, который всегда отстает на 20 секунд при нормальном трафике, заслуживает внимания.

Проверьте, является ли задержка преднамеренной

Прежде чем гоняться за ложным инцидентом, проверьте конфигурацию набора реплик:

rs.conf()

Задержанный узел настроен на отставание от первичного по замыслу. В современной конфигурации MongoDB ищите secondaryDelaySecs на узле. Этот узел полезен для некоторых сценариев восстановления, поскольку может сохранять более старую версию данных в течение короткого периода. Его не следует использовать для свежих чтений, и его ожидаемая задержка должна быть исключена из обычных оповещений о задержке.

Ошибка, которую я вижу в реальных операциях, — это оповещение о каждом задержанном узле, как если бы он был сломан. Оповещайте о задержке, превышающей настроенную. Если узел задержан на 1 час и показывает 1 час 5 минут задержки, реальная задержка составляет около 5 минут.

Когда окно oplog слишком мало

Oplog — это ограниченная коллекция в базе данных local. Вторичные узлы читают его и применяют операции по порядку. Если вторичный узел отстает настолько, что первичный узел больше не имеет необходимых записей oplog, обычное догоняние становится невозможным. Узлу обычно требуется начальная синхронизация или восстановление из подходящей резервной копии.

Вот почему окно oplog имеет значение. Вы хотите, чтобы оно покрывало больше, чем ожидаемое время простоя, обслуживание, перерывы в сети и пиковые всплески записи. Не существует универсального "правильного" размера oplog. Тихий кластер может хранить дни истории в небольшом oplog. Занятый кластер с интенсивными обновлениями может сжечь тот же размер за короткий период.

Если окно oplog уменьшается во время пикового трафика, увеличьте его до следующего окна обслуживания. В поддерживаемых версиях MongoDB используйте replSetResizeOplog, а не удаляйте и не создавайте заново local.oplog.rs. Удаление oplog на узле набора реплик — это маневр восстановления с высоким риском, а не обычный шаг настройки.

Выполните команду изменения размера на узле, чей oplog вы хотите изменить:

use admin
db.adminCommand({ replSetResizeOplog: 1, size: 10240 })

Значение size указано в мегабайтах. Значение 10240 означает примерно 10 ГБ. Изменяйте размер каждого узла по мере необходимости. В управляемых средах, таких как MongoDB Atlas, используйте поддерживаемый платформой путь конфигурации вместо того, чтобы предполагать прямой контроль над файловой системой или процессами.

После изменения размера проверьте новое окно под реальной нагрузкой записи. Больший oplog снижает вероятность выпадения из oplog, но не ускоряет применение операций медленным вторичным узлом.

Когда один вторичный узел медленный

Если отстает только один вторичный узел, войдите на этот хост и посмотрите на обычные системные симптомы. MongoDB часто обвиняют в том, что на самом деле является насыщением диска.

Используйте такие инструменты, как:

iostat -xz 1
vmstat 1
top
mongostat --host secondary.example.com:27017
mongotop --host secondary.example.com:27017

Высокая загрузка диска, высокое время ожидания или длинная очередь ввода-вывода обычно означают, что вторичный узел не может достаточно быстро записывать. Это может произойти, когда для вторичных узлов используется более дешевый тип экземпляра, когда EBS или сетевое хранилище имеют более низкую пропускную способность или когда резервное копирование и снимки файловой системы выполняются одновременно с пиковыми записями приложения.

ЦП также может иметь значение, особенно при сжатии, шифровании, перемещении документов, обслуживании индексов или рабочей нагрузке с множеством мелких обновлений. Нехватка памяти проявляется в виде страничных ошибок, перегрузки кэша и вторичного узла, который постоянно читает с диска, пытаясь применить записи oplog.

Практическое исправление обычно скучно: дайте вторичному узлу хранилище и ЦП, сопоставимые с первичным, уменьшите конкурирующую работу на этом хосте или переместите тяжелые чтения в другое место. Узел набора реплик — это не бесплатная отчетная мощность. Он все равно должен успевать за репликацией.

Когда чтения на вторичных узлах вызывают проблему

Масштабирование чтения с помощью вторичных узлов полезно, но его легко переусердствовать. Запрос панели мониторинга, который сканирует большую коллекцию, может конкурировать с применением oplog. Вторичный узел все еще может принимать чтения, но репликация отстает, потому что те же ЦП, кэш и диск используются для пользовательских запросов.

Проверьте профилировщик и текущие операции на отстающем узле:

db.currentOp({ active: true })

Если вы видите длительные чтения, задания агрегации или скрипты обслуживания, решите, должен ли этот вторичный узел действительно обслуживать эту рабочую нагрузку. Для отчетности может лучше подойти скрытый или выделенный вторичный узел. Для чтений приложения установите maxStalenessSeconds, чтобы драйвер избегал вторичных узлов, которые слишком сильно отстают.

Для критически важных для согласованности путей используйте чтения с первичного узла. Примеры включают состояние входа в систему, подтверждение заказа, изменение пароля, настройки учетной записи и все, где пользователь ожидает немедленно прочитать свою собственную запись. Чтения со вторичных узлов лучше всего подходят для данных, где допустима кратковременная устарелость.

Когда первичный узел создает всплески

Большие записи могут заставить здоровые вторичные узлы выглядеть сломанными. Массовые импорты, широкие многодокументные обновления, очистка TTL, большие удаления и изменения индексов могут создать всплеск активности oplog, на применение которого требуется время.

Ищите недавние операции на первичном узле:

db.currentOp({ active: true })

Также проверьте развертывания приложений, задания по восстановлению данных, обратные заполнения и запланированные задачи. Задержка репликации, которая начинается ровно в 02:00, часто не является загадкой. Это пакетное задание.

Когда вы контролируете задание, разбейте его на более мелкие части. Например, обновляйте документы по диапазонам _id, делайте паузы между пакетами и следите за задержкой во время выполнения задания. С bulkWrite неупорядоченные записи могут улучшить пропускную способность, но обработка ошибок должна быть явной, потому что сбои могут быть частичными. Цель не всегда в том, чтобы первичный узел завершил работу как можно быстрее. Цель состоит в том, чтобы позволить набору реплик поглотить работу без потери запаса восстановления.

Индексы и применение oplog

В обычном наборе реплик индексы реплицируются. Если индексы различаются между узлами из-за ручной работы, неудачного обслуживания или узла, который был восстановлен неправильно, вторичный узел может стать мучительно медленным при применении обновлений и удалений. Операции oplog может потребоваться найти документ, и без ожидаемого индекса вторичный узел может выполнить гораздо больше работы, чем первичный.

Сравните определения индексов на затронутых коллекциях:

db.orders.getIndexes()

Выполните ту же команду на первичном и отстающем вторичном узлах. Если они различаются, выясните причину, прежде чем вносить дополнительные изменения. Перестроение большого индекса само по себе может создать нагрузку, поэтому планируйте его в тихий период или перестройте узел из известного источника, если расхождение велико.

Не используйте старые советы, которые говорят, что фоновое построение индексов решает все проблемы репликации. Поведение построения индексов MongoDB менялось в разных версиях, и правильный операционный выбор зависит от вашей версии и топологии. Используйте текущую документацию сервера для точной версии, которую вы используете.

Сетевые проблемы обычно видны где-то еще

Сетевая задержка обычно проявляется в виде нестабильных пульсов, периодических ошибок или низкой пропускной способности между конкретными хостами или регионами. Базовые проверки все еще помогают:

ping primary.example.com
traceroute primary.example.com

Но низкая задержка ping не гарантирует достаточной пропускной способности. Репликация может быть ограничена пропускной способностью, потерей пакетов, проверкой брандмауэра, межрегиональными каналами или зашумленной общей сетью. Если задержка появляется только для удаленного вторичного узла, сравните его с вторичным узлом в том же регионе, что и первичный. Если узлы в том же регионе в порядке, а удаленный узел отстает, топология может предъявлять слишком высокие требования к каналу.

Для межрегиональных наборов реплик будьте честны в отношении компромисса. Они могут помочь в аварийном восстановлении, но они более подвержены ограничениям задержки и пропускной способности. Если удаленный узел предназначен для чтения, используйте контроль устаревания и тестируйте поведение при отказе, вместо того чтобы предполагать, что он будет вести себя как локальный вторичный узел.

Будьте осторожны с советами по перезапуску и повторной синхронизации

Перезапуск mongod может устранить временную проблему, но он также может усугубить инцидент, если узел был близок к выпадению из oplog. Перед перезапуском проверьте окно oplog и текущую задержку. Если узлу требуется два часа, чтобы догнать, а окно oplog составляет всего три часа во время пикового трафика, длительный перезапуск может оставить вас с начальной синхронизацией вместо догоняния.

Начальная синхронизация — это допустимый вариант восстановления, когда вторичный узел устарел, поврежден или потерял необходимую историю oplog. Это также дорого. Он копирует данные, строит индексы и потребляет сетевые и дисковые ресурсы от источников синхронизации. В производственной среде предпочтительнее добавлять или перестраивать по одному узлу за раз, чтобы набор реплик сохранял достаточное количество голосующих и несущих данные узлов для устойчивости к сбоям.

Если узел настолько отстал, что не может догнать, используйте свежую резервную копию или путь на основе снимков, который соответствует вашим операционным стандартам. Не удаляйте каталог данных, потому что так сказано в контрольном списке. Подтвердите, что узел является расходным, подтвердите, что набор реплик может выдержать перестройку, и подтвердите, что у вас достаточно окна oplog или надежного источника начальной синхронизации.

Оповещайте о том, что важно для пользователей и операторов

Хорошее оповещение — это не "задержка репликации больше 1 секунды" для каждой системы. Некоторые приложения могут терпеть 30 секунд для аналитических чтений. Другие не могут терпеть устаревшие чтения для состояния учетной записи. Пороги оповещения должны отражать вариант использования.

Полезные оповещения включают:

  • Задержка репликации выше допустимого уровня приложения в течение длительного периода.
  • Окно oplog ниже самого длинного ожидаемого интервала обслуживания или восстановления.
  • Вторичный узел в состоянии RECOVERING, STARTUP2 или нездоровом состоянии дольше, чем ожидалось.
  • Насыщение дискового ввода-вывода на любом узле, содержащем данные.
  • Сбои пульса или сетевые ошибки между узлами.

Панели мониторинга должны показывать задержку рядом с объемом записи, задержкой диска, ЦП, давлением памяти и пропускной способностью сети. Задержка сама по себе говорит вам, что есть проблема. Соседние графики обычно говорят вам, какая именно.

Практический порядок триажа

Когда вы на дежурстве, используйте этот порядок:

  1. Подтвердите, какие узлы отстают, с помощью rs.status().
  2. Проверьте, не является ли какая-либо задержка преднамеренной из-за secondaryDelaySecs.
  3. Проверьте окно oplog с помощью rs.printReplicationInfo().
  4. Сравните задержку с пиками записи, пакетными заданиями и недавними развертываниями.
  5. Проверьте диск, ЦП, память и локальную нагрузку запросов отстающего вторичного узла.
  6. Проверьте сетевые ошибки и задержку между затронутыми узлами.
  7. Решите, может ли узел догнать, нужно ли снять нагрузку, нужны ли дополнительные ресурсы или его необходимо перестроить.

Лучший результат обычно не является драматической командой. Это поиск узкого места и его устранение без создания расхождения данных. Задержка репликации MongoDB управляема, если рассматривать ее как сигнал о емкости и топологии, а не как общий сбой MongoDB.