Статьи

Простой MySQL Master HA с mysqlnd_ms

Этот пост был изначально написан  Джервином Реалом в MySQL Performance Blog.

Несколько дней назад я имел удовольствие представить PHP Users Group Philippines о mysqlnd_ms . Плагин mysqlnd, MySQL Master Slave, представляет собой прозрачный слой поверх расширения mysqlnd. Это позволяет вам выполнять разделение на чтение и запись, а подчиненное чтение — на балансировку нагрузки без необходимости что-либо менять в своем приложении. Но знаете ли вы, что вы также можете достичь высокой доступности с помощью этого плагина? В своей презентации я поделился двумя формами, используя асинхронную репликацию MySQL либо в конфигурации master-slave, либо в конфигурации master-master, в то время как вторая форма имеет весь первичный кластер, в который можно записывать все узлы.

Эта первая часть должна продемонстрировать, как вы можете достичь простого решения HA, используя первую форму. Во-первых, весь пример кода можно найти в моем репозитории GitHub . Итак, чтобы использовать плагин mysqlnd_ms, он использует дополнительный внешний файл конфигурации в формате JSON. Этот файл конфигурации будет определять ваши главные и подчиненные узлы, свойства аварийного переключения и любые фильтры (метод выбора соединения), которые вы хотите диктовать, как алгоритм предоставит вам соединение.

Давайте начнем с конфигурации mysqlnd_ms, которую я использовал, mysqlnd_ms_ms.ini:

{
  "primary": {
    "master": {
      "master_1": {
        "host": "127.0.0.1",
        "port": "33001"
      }
    },
    "slave": {
    }
  },
  "standby": {
    "master": {
      "master_1": {
        "host": "127.0.0.1",
        "port": "33002"
      }
    },
    "slave": {
    }
  }
}

Здесь у меня есть два приложения, одно из которых называется «основное», а другое — «резервное», для простоты я не определил никаких рабов. Два экземпляра MySQL, работающие через порт 33001 и 33002, находятся в конфигурации мастер-мастер.

mysqlnd_ms.enable = 1
mysqlnd_ms.disable_rw_split = 1
mysqlnd_ms.force_config_usage = 1
mysqlnd_ms.config_file = /home/revin/git/demo-me/phpugph201407/mysqlnd_ms_ms.ini

Это пользовательский INI-файл, который я использовал для тестов, master-slave.ini  . Первая строка просто включает плагин для использования. Вторая строка, mysqlnd_ms.disable_rw_split, инструктирует плагин, что я должен отправлять все запросы только мастеру, потому что у меня есть только мастера для этого теста.

Что касается PHP-скрипта, полную копию можно найти здесь , поскольку он немного длинен, я просто объясню логику того, что он делает.

  1. Чтобы запустить тест, он загружает тестовую таблицу через DROP, а затем создает запросы.
  2. Затем он входит в цикл for, где он выполнит INSERT, а затем SELECT для проверки вновь вставленной строки и дополнительной информации, такой как текущий идентификатор активного сервера и идентификатор соединения.
  3. Для каждой итерации цикла создается новый объект mysqli для имитации непостоянных соединений с сервером базы данных.
  4. Чтобы создать новое соединение, вызывается функция  connect_mysql,   которая возвращает объект mysqli в случае успеха. Здесь важно помнить, что mysqlnd_ms по умолчанию использует ленивые соединения, это означает, что когда объект mysqli создан, он еще не подключен к серверу.   Для запуска соединения вручную или вызова mysqli :: real_connect необходимо выполнить оператор типа  «SELECT 1»  . Даже  mysqli :: ping   не работает без первого, я открыл эту ошибку .
  5. После того, как объект mysqli возвращен, инструкция INSERT вызовет mysqlnd_ms, чтобы фактически установить соединение, а затем выполнить инструкцию. Вот где хорошая часть, если соединение не может быть установлено, функция query_write_mysql узнает и будет повторно запрашивать соединение у connect_mysql, на этот раз в функции connect_mysql, соединение с первичным будет повторено по крайней мере 10 раз, если Тип ошибки от предыдущего сбоя связан с соединением, например номерами ошибок  2002   и 2003  . Если соединение не может быть установлено после 10 повторных попыток, приложение создает файл стража как  / tmp / PRIMARY_HAS_FAILED   и повторяет подключение к вторичному устройству (ведомому или пассивному хозяину).

Вот пример запуска, мой первичный сервер имеет идентификатор сервера или 101, в то время как мой режим ожидания равен 102:

[revin@forge phpugph201407]$ php -c master-slave.ini master-slave-ng.php
Last value 0001 from server id 101 thread id 7
Last value 0003 from server id 101 thread id 8
37: [2002] Connection refused
Connection to host 'primary' failed: [0] Connection refused, retrying (1 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (2 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (3 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (4 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (5 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (6 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (7 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (8 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (9 of 10) in 3 seconds
Connection to host 'primary' failed: [0] Connection refused, retrying (10 of 10) in 3 seconds
The primary host 'primary' has failed after 30 seconds, failing over to standby!
52: [2002] Connection refused
Last value 0004 from server id 102 thread id 635
Last value 0006 from server id 102 thread id 636
Last value 0008 from server id 102 thread id 637
[...]

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

    • Если у вас есть конфигурация «ведущий-ведомый», вы просто стреляли по своему основному (ведущему) в ногу во время аварийного переключения. Возможно, вам придется восстановить данные утром.
    • Если вместо этого у вас есть мастер-мастер, вы можете просто вернуть основной мастер в оперативный режим, перехватить его при репликации, а затем удалить    файл / tmp / PRIMARY_HAS_FAILED, чтобы переключить ваше приложение на него.
    • Использование    сторожевого файла / tmp / PRIMARY_HAS_FAILED является зачаточным, и это не единственный способ. Вам следует подумать об отправке уведомлений себе, когда происходит переключение при сбое, потому что этот метод требует вмешательства человека, чтобы вернуть основной мастер во вращение.

Тот же эффект может быть достигнут с немного большим количеством кода, но вы уже можете использовать плагин с меньшими затратами.

Я также протестировал плагин во второй форме, где вы можете писать нескольким мастерам, используя Percona XtraDB Cluster. Я нашел там несколько интересных вопросов, так что следите за обновлениями.