Статьи

MongoDB: отставание репликации и факты жизни

Примечание куратора. Содержание этой статьи изначально было написано в блоге MongoLab.

Итак, вы проверяете свое последнее удивительное приложение однажды — оно действительно набирает обороты! Вы гордитесь его временем безотказной работы, отчасти благодаря реплике MongoDB, установленной под ним. Но сейчас … что-то не так. Пользователи жалуются, что некоторые их данные пропали без вести. Другие замечают, что удаленный материал неожиданно появился снова. Что происходит?!?

Не волнуйтесь … мы доберемся до сути этого! При этом мы рассмотрим источник риска, который легко пропустить в приложении MongoDB:  задержка репликации —  что это значит, почему это происходит, и что вы можете с этим поделать.

Вот что мы собираемся охватить:

Продолжая эту предостерегающую историю … Серьезно, ВТФ ?! Вы все делали правильно!

Используя MongoDB с хорошо разработанной схемой и любовно настроенными индексами, серверная часть вашего приложения обрабатывает тысячи транзакций в секунду без проблем. У вас есть несколько узлов, расположенных в наборе реплик без единой точки отказа. Подключения драйвера Mongo вашего уровня приложений осведомлены о наборе реплик и могут отслеживать изменения в основном узле во время восстановления после отказа. Все критические записи являются «безопасными». Ваше приложение работает без перерывов в течение почти шести месяцев! Как это могло случиться?

Эта тревожная ситуация имеет признаки коварного врага в области управления данными с высокой доступностью: неконтролируемая задержка репликации .

Очень важно внимательно следить за набором реплик MongoDB на предмет задержки репликации.

Что такое задержка репликации?

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

Факты жизниВ идеальном мире репликация данных была бы мгновенной; но на самом деле, благодаря надоедливым законам физики, некоторая задержка неизбежна — это  факт жизни . Мы должны уметь рассуждать о том, как это влияет на нас, чтобы надлежащим образом справляться с этим явлением. Давайте начнем с определений …

Для данного вторичного узла задержка репликации — это задержка между временем, когда операция выполняется на первичном сервере, и временем, когда та же самая операция применяется на вторичном сервере.

Для набора реплик в целом задержка репликации является (для большинства целей) наименьшей задержкой репликации среди всех ее вторичных узлов.

В отлаженном наборе реплик все вторичные серверы внимательно следят за изменениями на первичном сервере , извлекая каждую группу операций из оплога и воспроизводя их примерно так быстро, как они происходят. То есть задержка репликации остается максимально близкой к нулю. Чтения из любого узла тогда достаточно последовательны; и, если текущий первичный сервер станет недоступным, вторичный сервер, который принимает на себя роль ПЕРВИЧНАЯ, сможет обслуживать клиентов набор данных, который практически идентичен исходному.

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

Почему отставание проблематично?

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

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

    • Во время повторной синхронизации в вашем кластере не будет избыточности действительного вторичного устройства; кластер не вернется к высокой доступности, пока не будет скопирован весь набор данных.
    • Если вы делаете резервные копии только со своего дополнительного устройства (что мы настоятельно рекомендуем), резервные копии должны быть приостановлены на время повторной синхронизации.
  • Задержка репликации повышает вероятность того, что результаты любых операций чтения, распределенных по вторичным серверам, будут противоречивыми.
  • «Безопасная» запись с «w»> 1, т. Е. Требующая, чтобы несколько узлов подтвердили запись до ее возврата, будет вызывать задержку, пропорциональную текущей задержке репликации, и / или может превышать время ожидания.

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

Что вызывает вторичное отставание?

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

Вторичный слабый

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

Bursty пишет

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

db.coll.update({x: 7}, {$set: {y: 42}}, {multi: true}}

может разместить неисчислимое количество отдельных операций «обновления» в оплоге первичного сервера. Чтобы не отставать, вторичный сервер должен извлекать эти операции (не более 4 МБ за раз для каждой getMoreкоманды!), Считывать в ОЗУ любой индекс и страницы данных, необходимые для удовлетворения каждого _idпоиска (помните: каждая запись оплога ссылается на один целевой документ _id; исходный запрос о «х» никогда не отражается напрямую в оплоге) и, наконец, выполняет обновление оп, изменяя документ и помещая соответствующую запись в его оплог; и он должен делать все это за то же время, что первичный делает только последний шаг. Умноженное на достаточно большое количество операций, это несоответствие может составить заметное отставание.

Карта / уменьшить выход

Конкретный тип сценария экстремальной записи может быть такой командой:

db.coll.mapReduce( ... { out: other_coll ... })

Построение индекса

Вас может удивить, что даже если вы создадите индекс в фоновом режиме для основного объекта, он будет создан на переднем плане для каждого дополнительного элемента. В настоящее время нет возможности создавать индексы в фоновом режиме на вторичных узлах (см. SERVER-2771 ). Поэтому всякий раз, когда вторичный сервер строит индекс, он на время блокирует все другие операции , включая репликацию. Если индекс строится быстро, это может не быть проблемой; но долгосрочные построения индекса могут быстро проявиться как существенная задержка репликации.

Вторичный заблокирован для резервного копирования

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

Вторичный вне форума

Точно так же, если вторичный сервер не работает или по какой-либо причине не может связаться с первичным сервером, он не может добиться прогресса в работе с резервом репликации. Когда он присоединяется к набору реплик, задержка репликации будет естественно отражать затраченное время.

Как мне измерить отставание?

Запустите команду db.printSlaveReplicationInfo ()

Чтобы определить текущую задержку репликации вашего набора реплик, вы можете использовать mongoоболочку и запустить db.printSlaveReplicationInfo()команду.

rs-ds046297:PRIMARY db.printSlaveReplicationInfo()
 
source: ds046297-a1.mongolab.com:46297
syncedTo: Tue Mar 05 2013 07:48:19 GMT-0800 (PST)
      = 7475 secs ago (2.08hrs)
source: ds046297-a2.mongolab.com:46297
syncedTo: Tue Mar 05 2013 07:48:19 GMT-0800 (PST)
      = 7475 secs ago (2.08hrs)

Больше 2 часов — ага, разве это не много? Может быть!

Видите ли, эти времена «syncedTo» не имеют ничего общего с часами на стене; это просто отметка времени последней операции, которую реплика скопировала из PRIMARY. Если последняя операция записи на PRIMARY произошла 5 минут назад, то да: 2 часа — это много. С другой стороны, если последний опер был 2,08 часа назад, то это золотой!

Чтобы заполнить этот недостающий фрагмент истории, мы можем использовать db.printReplicationInfo()команду.

rs-ds046297:PRIMARY db.printReplicationInfo()
 
configured oplog size:   1024MB
log length start to end: 5589secs (1.55hrs)
oplog first event time:  Tue Mar 05 2013 06:15:19 GMT-0800 (PST)
oplog last event time:   Tue Mar 05 2013 07:48:19 GMT-0800 (PST)
now:                     Tue Mar 05 2013 09:53:07 GMT-0800 (PST)

Давайте посмотрим … «ОПЛОГ в последний раз» — первичное «syncedTo» = 0.0. Ура.

Каким бы забавным это ни было вычитание, оно редко требуется. Если есть постоянный поток операций записи, последняя операция на ПЕРВИЧНОМ, как правило, была совсем недавней. Таким образом, фигура типа «2,08 часа», вероятно, должна поднять брови; вместо этого вы ожидаете увидеть хорошее низкое число — возможно, несколько секунд. И, увидев небольшое число, не нужно будет уточнять его контекст второй командой.

Изучите график «repl lag» в MMS

Вы также можете просмотреть последние и исторические задержки репликации с помощью службы мониторинга MongoDB (MMS) от 10gen. На вкладке «Состояние» каждого ВТОРОГО узла вы найдете график задержки :

Снимок экрана 2013-03-10 в 12.13.22

Как мне следить за задержкой?

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

Вот несколько способов убедиться, что об этом позаботятся:

  • Если MongoLab размещает ваш набор реплик, расслабьтесь! Для любого многоузлового набора высокодоступных реплик, который мы размещаем для вас, вы можете отслеживать задержки репликации в нашем пользовательском интерфейсе, и по умолчанию вы будете получать автоматические оповещения, когда задержка репликации превышает 10 минут.
  • Вы также можете настроить оповещение с помощью системы MMS. Его  замечательные новые функции позволяют настроить оповещение о задержке репликации:

Снимок экрана 2013-03-10 в 1.18.56 PM

Что я могу сделать, чтобы минимизировать отставание?

Из вежливости (для них или для себя) мы хотели бы сделать жизнь автоматов, отслеживающих отставание, максимально скучной. Вот несколько советов:

Совет № 1: убедитесь, что ваш вторичный имеет достаточно лошадиных сил

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

В частности, ВТОРИЧНЫЙ узел должен иметь достаточную пропускную способность сети, чтобы он мог извлекать операции из оплога PRIMARY примерно с той скоростью, с которой они созданы, а также с достаточной пропускной способностью хранилища, чтобы он мог применять операции — то есть читать любые затронутые документы и их записи индекса в RAM и зафиксируйте измененные документы обратно на диск — с той же скоростью. Процессор редко становится узким местом, но это может потребоваться, если существует много индексных ключей для вычисления и вставки для документов, которые добавляются или изменяются.

Совет № 2: Рассмотрите возможность корректировки вашей проблемы записи

Ваш вторичный сервер может отставать просто потому, что оплог вашего первичного сервера заполняется быстрее, чем он может быть реплицирован. Даже с таким же мускулистым ВТОРИЧНЫМ узлом, PRIMARY всегда будет способен разместить 4 МБ в своем оплоге с отображением в памяти за долю времени, в течение которой эти же 4 МБ должны будут проходить через соединение TCP / IP.

Одним из эффективных способов применения некоторого обратного давления к основному может быть корректировка вашей проблемы с записью .

Если вы в настоящее время используете запись с записью, которая не подтверждает запись (так называемый режим «запусти и забывай»), вы можете изменить свою задачу записи, чтобы потребовать подтверждение от первичного ( w:1) и / или запись в журнал первичного ( j:true). Это замедлит скорость, с которой соответствующее соединение может генерировать новые операции, требующие репликации.

В других случаях может быть целесообразно использовать «w»> 1 или «w», установленный в « majority», чтобы гарантировать, что каждая запись в кластер будет реплицирована на более чем один узел, прежде чем команда вернется. Требование подтверждения того, что запись реплицирована на вторичные серверы, будет эффективно гарантировать, что эти вторичные серверы догнали (по крайней мере, до отметки времени этой записи), прежде чем следующая команда в том же соединении может произвести больше операций в резерве.

Как уже упоминалось ранее, выбор наиболее подходящей задачи записи для требований к долговечности данных вашего приложения — или для определенных критических операций записи в приложении — это то, о чем вы должны подумать независимо от проблемы задержки репликации, на которой мы здесь сосредоточены. Но вы должны знать о взаимосвязи: так же, как гарантия долговечности w> 1 может использоваться в качестве средства принудительного применения периодической «контрольной точки» при репликации, чрезмерная задержка репликации может проявляться как удивительно высокая задержка (или время ожидания) для Эта очень редкая критическая операция записи, когда вы использовали « w: majority», чтобы убедиться, что она действительно зафиксирована.

Настроить по вкусу

Наличие серверов, подтверждающих каждую запись, может сильно повлиять на пропускную способность системы. Если это имеет смысл для вашего приложения, вы можете амортизировать этот штраф, выполняя вставки в пакетах, требуя подтверждения только в конце каждого пакета. Чем меньше пакет, тем больше обратное давление на ПЕРВИЧНУЮ скорость создания данных и, соответственно, большее потенциальное негативное влияние на общую пропускную способность.

Не переусердствуйте

Использование большого значения для w само по себе может быть проблематичным. Он представляет собой требование, чтобы узлы w закончили работу с существующим резервом до возврата команды. Таким образом, если задержка репликации высока (в смысле большого объема данных, ожидающих копирования) при выдаче команды записи, время выполнения команды будет пропорционально высоким. Кроме того, если достаточное количество узлов отключается, так что «w» не может быть удовлетворено, вы фактически заблокировали свою базу данных. Это в основном противоположность «высокой доступности».

Совет № 3: Планирование построения индекса

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

Совет № 4: делать резервные копии без блокировки

Ранее мы обсуждали технику блокировки вторичного устройства для резервного копирования. Здесь можно рассмотреть другие альтернативы, включая моментальные снимки файловой системы и резервное копирование «на время» с использованием параметра «—oplog» mongodump без блокировки. Они предпочтительнее блокировки вторичного устройства во время периода активной записи, если есть вероятность, что вы будете использовать вторичное устройство для чего-либо, кроме резервного копирования.

Совет № 5: убедитесь, что закрытые коллекции имеют поле _id и уникальный индекс

Надежная репликация невозможна, если на _idполе нет уникального индекса . До MongoDB версии 2.2 , блокированные коллекции не имеют _idполя или индекс по умолчанию. Если у вас есть такая коллекция, вы должны создать указатель на _idполе, указав unique: true. Невыполнение этого требования может в некоторых ситуациях привести к полной остановке репликации . Так что … это не должно рассматриваться как необязательное.

Совет № 6: Проверьте наличие ошибок репликации

Если вы видите, что задержка репликации только увеличивается (и никогда не падает), возможно, в вашем наборе реплик возникли ошибки репликации. Чтобы проверить на наличие ошибок, запустите rs.status()и посмотрите на errmsgполе в результате. Кроме того, проверьте файл журнала вашего дополнительного устройства и найдите там сообщения об ошибках.

Один конкретный пример: если вы видите «RS102 слишком устаревший, чтобы его догнать» в mongodb.log вторичного сервера или в errmsgполе во время работы rs.status(), это означает, что вторичный сервер отстал настолько далеко, что у первичного сервера недостаточно истории (его « размер оплога »), чтобы привести его в соответствие. В этом случае вашему вторичному устройству потребуется полная повторная синхронизация с первичного.

В целом, однако, что вы делаете в ответ на ошибку, зависит от ошибки. Иногда вы можете просто перезапустить процесс mongod для своего дополнительного устройства; но в большинстве случаев вам необходимо понять причину ошибки, прежде чем вы сможете решить проблему.

Не позволяйте задержке репликации застать вас врасплох.

В конце концов, задержка репликации является еще одним источником риска в любой системе высокой доступности, которую мы должны понять и спроектировать. Нахождение правильного баланса между производительностью и «безопасностью» операций записи — упражнение в управлении рисками — «правильный» баланс будет разным в разных ситуациях. Например, для приложения с ограниченным бюджетом и случайными скачками объема записи вы можете решить, что большая задержка репликации после этих скачков является приемлемой, учитывая цели приложения, и поэтому имеет смысл вторичный вторичный ресурс. С другой стороны, для приложения, в котором каждая запись является драгоценной и священной, требуемая забота о «мажорной» записи будет означать, что вы практически не допускаете отставание репликации от максимально возможного минимума.Хорошей новостью является то, что MongoDB делает все это очень настраиваемым, даже в зависимости от операции.

Мы надеемся, что эта статья даст вам некоторое представление о феномене задержки репликации, который позволит вам рассуждать о риске, который он представляет для приложения MongoDB с высокой доступностью, и вооружил вас некоторыми инструментами для управления им. Как всегда, дайте нам знать, если мы можем помочь !