4.10.7 Часто задаваемые вопросы по репликации

Вопрос: Как сконфигурировать подчиненный сервер, если головной сервер уже запущен, и я не хочу его останавливать?

Ответ: Есть несколько возможностей. Если имеется резервная копия головного сервера в некоторой точке, имя двоичного журнала и сдвиг (от вывода SHOW MASTER STATUS), соответствующий образу, выполните следующие действия:

  • Удостоверьтесь, что подчиненному серверу назначен уникальный идентификатор.
  • Выполните команды CHANGE MASTER TO MASTER_HOST='master-host-name', MASTER_USER='master-user-name', MASTER_PASSWORD='master-pass', MASTER_LOG_FILE='recorded-log-name', MASTER_LOG_POS=recorded_log_pos
  • Выполните команду SLAVE START

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

    FLUSH TABLES WITH READ LOCK
  • gtar zcf /tmp/backup.tar.gz /var/lib/mysql (или разновидность данной команды)
  • SHOW MASTER STATUS - удостоверьтесь в том что вывод этой команды сохранен - он пригодится позже
  • UNLOCK TABLES

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

В версии 4.0.0 и выше можно также использовать команду LOAD DATA FROM MASTER. Это удобная команда, которая создает образ, восстанавливает его на подчиненном сервере и сразу же корректирует имя журнала и сдвиг на подчиненном сервере. Именно команду LOAD DATA FROM MASTER можно рекомендовать как способ установки подчиненного сервера. Имейте в виду, однако, что при использовании данной команды чтение может быть блокировано на долгое время. В настоящее время эта команда еще не реализована настолько эффективно, как бы нам хотелось. Если имеются большие таблицы, пока лучше использовать локальный архив tar после выполнения команды FLUSH TABLES WITH READ LOCK.

В: Должен ли подчиненный сервер постоянно быть подсоединен к головному серверу?

О: Нет, не должен. Можно прекращать работу подчиненного сервера или оставлять его отсоединенным на несколько часов или даже дней, затем повторно подгонять подчиненный сервер к произошедшим обновлениям, и затем снова отсоединять или останавливать его на некоторое время. Таким образом можно, например, использовать установку "головной-подчиненный" через коммутационную связь, работающую в течение коротких периодов времени. При такой реализации, однако, нельзя гарантировать, что в какое-либо заданное время подчиненный сервер будет синхронизирован с головным сервером, если вы для этого не примете специальных мер. В будущем будет добавлена опция для блокировки головного сервера до тех пор, пока хотя бы один подчиненный сервер находится в синхронизации с ним.

В: Как заставить головной сервер блокировать обновления, пока происходит соединение с подчиненным сервером?

О: Выполните следующие команды:

  • Головной сервер: FLUSH TABLES WITH READ LOCK
  • Головной сервер: SHOW MASTER STATUS - запомните имя журнала и сдвиг
  • Подчиненный сервер: SELECT MASTER_POS_WAIT('recorded_log_name', recorded_log_offset) После выполнения этой команды подчиненный сервер будет синхронизирован с головным сервером
  • Головной сервер: UNLOCK TABLES - теперь головной сервер может продолжить обновления.

В: Почему иногда после перезапуска подчиненного сервера я вижу более одного потока Binlog_Dump на головном сервере?

О: Поток Binlog_Dump является непрерывным процессом, который обрабатывается сервером следующим способом:

  • Осуществляется захват обновлений.
  • Если не остается больше обновлений, входит в число pthread_cond_wait(), откуда его можно "пробудить", выполнив следующее обновление, или уничтожить.
  • После пробуждения проверяется причина пробуждения. Если нет причины прекращения цикла, цикл Binlog_dump продолжается.
  • При возникновении какой-либо фатальной ошибки, такой как обнаружение "мертвого" клиента, цикл прекращается.

Таким образом, если поток подчиненного сервера прекратится на подчиненном сервере, соответствующий поток Binlog_Dump на головном сервере не будет замечать этого, пока не произойдет по крайней мере одного обновления на головном сервере (или уничтожения потока), которое необходимо, чтобы пробудить его из числа pthread_cond_wait(). Тем временем подчиненный сервер может открыть другое соединение, результатом которого будет другой поток Binlog_Dump.

Эта проблема не должна присутствовать в версии 3.23.26 и более поздних версиях. В версии 3.23.26 для каждого репликационного сервера добавляется идентификатор server-id, и теперь все старые процессы-зомби на головном сервере уничтожаются при присоединении нового репликационного потока из того же самого подчиненного сервера.

В: Как прокручивать журналы репликации?

О: В версии 3.23.28 нужно использовать команду PURGE MASTER LOGS TO после определения тех журналов, которые должны быть удалены, и выборочно сделать резервные копии этих журналов. В более ранних версиях этот процесс намного более трудоемкий, и не может быть безопасно выполнен без остановки всех подчиненных серверов, если планируется повторное использование имен журналов. Нужно будет остановить потоки подчиненного сервера, отредактировать индексный файл двоичного журнала, удалить все старые журналы, перезапустить головной сервер, запустить потоки подчиненного сервера и затем удалить файлы старых журналов.

В: Как сделать апгрейд сервера во время репликации?

О: Если модернизируемая версия ниже 3.23.26, нужно лишь блокировать таблицы головного сервера, позволить подчиненному серверу подогнать обновления, затем выполнить команду FLUSH MASTER на головном сервере и команду FLUSH SLAVE на подчиненном сервере, чтобы очистить журналы, затем перезапустить новые версии на головном и подчиненном серверах. Обратите внимание: подчиненный сервер может останавливаться на некоторое время - пока головной сервер записывает в журнал все обновления, подчиненный сервер будет способен подогнать обновления как только сможет начать работу и подсоединиться к головному серверу.

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

Q: Какие проблемы могут возникать при установке двухсторонней репликации?

A: В настоящее время для репликаций MySQL не поддерживается никакого протокола блокировки между головным и подчиненным сервером, который обеспечивал бы неделимость распределенных (междусерверных) обновлений. Другими словами, клиент A может делать обновления на головном сервере 1, и в это же время, перед тем, как эти обновления скопируются на головной сервер 2, клиент B может делать обновления на головном сервере 2, из-за которых обновления клиента A будут выполняться не так, как на головном сервере 1 компании. Таким образом, когда обновления, сделанные клиентом A, будут перенесены на головной сервер 2, таблицы, полученные в результате, будут отличаться от таблиц на головном сервере 1. В этом случае таблицы на двух серверах будут разными, даже если обновления, произошедшие на головном сервере 2, также будут скопированы на головной сервер 1. Отсюда следует, что не стоит соединять в цепочку два сервера двусторонней репликационной связью, если вы не уверены, что обновления будут безопасно выполняться в любом порядке, или если вы не обрабатываете каким-либо образом такие неупорядоченные обновления где-либо в клиентском коде.

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

Q: Как использовать репликацию для повышения производительности системы?

A: Установите один сервер как головной и направляйте все записи к нему, а также сконфигурируйте такое количество подчиненных серверов, на какое у вас хватит средств и дискового пространства, и распределите чтение между головным сервером и подчиненными серверами. Можно также запустить подчиненные серверы с опциями --skip-bdb, --low-priority-updates и --delay-key-write-for-all-tables, чтобы получить увеличение скорости на подчиненном сервере. В данном случае подчиненный сервер для повышения скорости будет использовать нетранзакционные таблицы MyISAM вместо таблиц BDB.

Q: Что нужно сделать, чтобы подготовить свой клиентский код для использования репликации, повышающей производительность?

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

  • Safe_writer_connect()
  • Safe_reader_connect()
  • Safe_reader_query()
  • Safe_writer_query()

Префикс safe_ означает, что функция будет нести ответственность за обработку всех возникающих ошибок.

После этого нужно преобразовать клиентский код так, чтобы он использовал библиотеку оболочки. Вначале все это покажется лишней головной болью, но в конечном счете ваши усилия окупятся. Все приложения, построенные в соответствии с приведенной выше схемой, смогут "приобщиться" к преимуществам решения один головной/много подчиненных. Код будет гораздо легче поддерживать, а добавление опций поиска неисправностей станет тривиальным. К примеру, если вам захочется узнать, какой запрос среди многих тысяч возвращает ошибку или реализовать регистрацию продолжительности выполнения каждого запроса, то понадобится изменить всего пару функций. Если у вас уже написано много кода, то для автоматизации задачи его преобразования можно использовать написанную Монти утилиту replace, которая имеется в стандартном дистрибутиве MySQL, или написать собственный сценарий на Perl. Остается надеяться, что ваш код удовлетворяет некоторой распознаваемой схеме. В противном случае будет лучше переписать его каким-либо образом, или, по крайней мере, вручную подогнать его под схему.

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

Q: В каких случаях репликация MySQL может улучшить производительность системы, и насколько?

A: Механизм репликации MySQL наиболее эффективен для системы, где чтение производится часто, а запись - редко. Теоретически, используя установку один головной/много подчиненных, можно наращивать ее, добавляя подчиненные серверы, пока не исчерпается пропускная способность сети или количество обновлений не вырастет настолько, что головной сервер не сможет обрабатывать их.

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

Предположим, что загрузка системы состоит из 10% операций записи и 90% операций чтения, и известно что max_reads = 1200 - 2 * max_writes, или другими словами, наша система, не делая записей, может делать 1200 операций чтения за секунду, средняя скорость записи вдвое ниже, чем средняя скорость чтения, а зависимость между этими величинами линейная. Предположим, что головной сервер и подчиненный сервер имеют одинаковую мощность, и имеется N подчиненных серверов и 1 головной. Тогда для каждого сервера (головного или подчиненного) имеем:

reads = 1200 - 2 * writes (по результатам тестирования)

reads = 9 * writes / (N + 1) (операции чтения распределяются по серверам, но запись выполняются на всех серверах)

9 * writes/(N+1) + 2 * writes = 1200

writes = 1200/(2 + 9/(N+1)

Таким образом, если N = 0, что означает отсутствие репликации, система может обрабатывать 1200/11, т.е. около 109 записей в секунду (а число операций чтения, в соответствии с нашими допущениями для данной системы, будет в 9 раз больше, чем число операций записи).

Если N = 1, можно получить 184 операций записи в секунду.

Если N = 8, можно получить до 400 операций записи в секунду.

Если N = 17, то - 480 операций записи в секунду.

В конечном счете, если N приближается к бесконечности (а бюджет к минус бесконечности), можно получить около 600 записей в секунду, при этом производительность системы увеличится приблизительно в 5,5 раз. Однако при использовании лишь 8 серверов производительность уже увеличивается почти в 4 раза.

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

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

Q: Как использовать репликацию для обеспечения избыточности и хорошей доступности?

A: С учетом сегодняшних возможностей репликации нужно будет установить головной сервер и подчиненный сервер (или несколько подчиненных серверов), и написать сценарий для мониторинга головного сервера - для определения, включен ли он, и уведомления приложения и подчиненных серверов об изменениях головного сервера в случае ошибки. Ниже приведено несколько советов:

  • Для уведомления подчиненного сервера об изменениях головного сервера используйте команду CHANGE MASTER TO.
  • Хороший способ информирования приложений о местоположении головного сервера - иметь на головном сервере динамической компонент DNS. При использовании bind для динамического обновления DNS можно применять nsupdate.
  • Запустите подчиненные серверы с опцией log-bin, но без log-slave-updates. Таким образом подчиненный сервер будет готов стать головным сервером после выполнения команд STOP SLAVE; RESET MASTER и CHANGE MASTER TO на других подчиненных серверах. Указание этой опции обеспечит также возможность перехвата ложных обновлений, которые могут происходить из-за ошибочной конфигурации подчиненного сервера (в идеале можно настроить права доступа таким образом, чтобы никакой клиент не мог производить обновления на подчиненном сервере иначе, чем через поток подчиненного сервера) в сочетании с ошибками в клиентских программах (они никогда не должны производить обновления на подчиненном сервере непосредственно).

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