Multi-Master репликация в MySQL

    В данной статье будет рассмотрен процесс развертывания отказоустойчивой подсистемы баз данных на базе MySQL сервера.


    Перед прочтением советую прочитать эту статью.

    На работе встал вопрос по созданию зеркала сайта для другого региона (Азия). Т.к. время прохождения пакетов туда довольно большое, сказывается географическая удалённость, да и Великий Китайский Файервол тоже еще не отменили, то было решено создать зеркало в азиатском регионе. С переносом движка проблем не предвиделось, файлы пользователей можно спокойно синхронизировать через rsync, а вот с базой данных наметилась проблема. Как быть если пользователь добавил объект в Азии? Надо, чтобы этот объект был виден не только пользователям локального зеркала, но и всем остальным.

    Изучение вопроса я начал с кластеризации MySQL. Выяснилось много интересных подробностей. Например, то, что версия сервера входящая в дистрибьютив Ubuntu(её я использую на тестовом стенде) не поддерживает NDB(Network Data Base) Storage Engine. Необходимо поставить либо версию mysql-max, которая по слухам поддерживает NDB, либо mysql-cluster. Самое интересно что deb-пакетов для них нет, так что надо либо ставить из бинарников, либо из исходников. Установка из бинарноков у меня не получилась. Я никак не мог удалить старый сервер (пакет удаляется, а файлы все на месте). Честно прокулупавшись с этим два дня я бросил сию затею. Из исходников компилировать не пробовал, но думаю это самый лучший вариант.

    Master-Master replication


    image

    Вообщем следующим этапом изысканий была репликация. Тут то я и понял, что это то, что мне нужно. Есть центральный сервер, и есть реплика в регионе, которая будет таскать с него обновления. Но надо сделать так, чтобы и обновления с реплики попадали в основную базу. Получается, необходимо сделать master-master репикацию. Т.е. первый сервер будет мастером для второго(второй подключапется к нему как слейв), а второй будет мастером для первого(первый подключается к нему как слейв). Испытания проводил на виртуалках на базе Ubuntu Server 9.10

    Итак, сервер ubuntu1(192.168.0.21), конфиг mysql:

    [mysqld]
    # номер сервера, у всех реплицируемых серверов они должны быть уникальными
    server-id = 1

    # конфигурация серера, как мастера
    log-bin = /var/lib/mysql/mysql-bin

    # конфигурация сервера, как слейва
    relay-log = /var/lib/mysql/mysql-relay-bin
    relay-log-index = /var/lib/mysql/mysql-relay-bin.index
    replicate-do-db = test_db
    master-host=192.168.0.22 #ubuntu2
    master-user=replication
    master-password=password_of_user_replication
    master-port=3306


    Для второго аналогично, только другой ip мастера и номер сервера.

    Теперь перезапускаем сервера. Потом создаем юзера replication, даем ему права на репликацию:
    mysql@ubuntu1> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'replication'@'192.168.0.22' IDENTIFIED BY 'password';
    mysql@ubuntu2> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'replication'@'192.168.0.21' IDENTIFIED BY 'password';


    Дальше необходимо привязать слейвов к своим мастерам. Делается это так:
    Привязка ubuntu1 как мастера к ubuntu2:
    Сначала блокируем запись в базу.
    mysql@ubuntu1> SET GLOBAL read_only = OFF;
    Подробнее об этом можно прочитать здесь

    mysql@ubuntu1> show master status;
    +------------------+----------+--------------+------------------+
    | File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
    +------------------+----------+--------------+------------------+
    | mysql-bin.000006 | 7984 | | |
    +------------------+----------+--------------+------------------+
    1 row in set (0,00 sec)


    Берем оттуда значение File и position и осуществляем подключение мастера.
    mysql@ubuntu2> slave stop; # на всякий случай
    mysql@ubuntu2> CHANGE MASTER TO MASTER_HOST = "192.168.0.22", MASTER_USER = "replication", MASTER_PASSWORD = "password_of_user_replication", MASTER_LOG_FILE = "mysql-bin.000006", MASTER_LOG_POS = 7984;
    mysql@ubuntu2> slave start;

    После этого проверить подключение можно либо через
    mysql@ubuntu2>load data from master;
    либо через
    mysql@ubuntu2> show slave status;
    Если ошибок нет, мастер подключен.
    Теперь подключаем этот сервер в качестве мастера для первого (делается точно также).
    В итоге мы имеем систему из двух серверов, вносим изменение на любой сервер и оно реплицируется на второй.

    Multi-Master replication


    Но этого мне показалось мало. А если захочется добавить третий сервер? Сначала я думал про линейную топологию
    ubuntu1 <-> ubuntu2 <-> ubunt3 и даже топологию звезда. Но увы, это было далеко от реальности. MySQL не позволяет одному слейву иметь несколько мастеров. А в линейной топологии таким будет ubuntu2. Если кто-то знает, как можно прявязать один слейв к нескольким мастерам буду благодарен за информацию.
    Звезда не подходит, линейная тоже, остается кольцо. А попробую, подумал я.

    image

    Получается что в данной схеме каждый сервер имеет только одного мастера, а благодаря тому, что кольцо замкнуто, обновления дойдут до всех серверов из любой точки кольца. Тут важно упомянуть, что чтобы мастер передавал на слейв не только свои собственные обновления, но и обновления своего мастера надо добавить в [mysqld] секцию my.cnf строку:
    log-slave-updates
    соответственно в конфиг каждого сервера.

    Проверил, и был приятно удивлен, что схема заработала. Где бы не меняли базу, изменения реально реплицируются на остальные сервера.

    Автоинкрементные поля


    При одновременном добавлении новых строк, содержащих автоинкрементные поля, на разные мастер-сервера может возникнуть конфликт. Чтобы такого не происходило, надо изменить шаг последовательности автоинкрементов на серверах БД.
    • auto_increment_increment определяет шаг изменения AUTO_INCREMENT .
    • auto_increment_offset определяет начальное значение инкремента

    Подобрав правильные(не конфликтующие) значения этих параметров на разных мастерах, сервера, используемые в мульти-мастер конфигурации будут использовать неконфликтующие значения AUTO_INCREMENT при вставки записей. Например для N мастер-серверов, установим такие значения:
    • Установим auto_increment_increment в N на каждом мастере.
    • На каждом из N мастеров ставим разные значения auto_increment_offset, используя 1, 2,…, N.

    Например, используя auto_increment_increment = 10 и auto_increment_offset=3, будут сгенерированы следующие значения поля 3, 13, 23. А используя 10, 7, будут такие 7, 17, 27, и т.д.

    Отказоусточивость


    После всего этого не мог не попробовать уронить один из серверов. Выдергиваем питание из ubuntu2. Потом загружаем его. Что же получается?
    При последующих изменениях на ubnutu2 они без проблем тиражируются на ubuntu3 и ubuntu1. Но изменения вносимые на ubuntu2 на остальные сервера не тиражируеются. Смотрим show slave status на ubuntu3 и видим, что он потерял своего мастера.
    В качестве рецепта, могу предложить следующее:
    После старта сервера БД выполнять отвязывание мастера от слейва текущего сервера, и привязывание его вновь. Т.к. в топологии кольцо слейв у любого сервера один, это упрощает задачу. Алгоритм будет примерно такой:
    1. ubuntu2 выключился по питанию и загрузился вновь
    2. ubuntu2 у себя:
    — блокирует реплицируемую базу на запись: mysql@ubuntu2> SET GLOBAL read_only = OFF;
    — смотрит название и позицию в логе: mysql@ubuntu2> show master status;
    2. ubuntu2 через mysql клиент заходит на своего слейва (ubuntu3)
    — отвязывает себя от слейва mysql@ubuntu3> slave stop;
    — объявляет себя мастером: mysql@ubuntu3> CHANGE MASTER TO MASTER_HOST = "192.168.0.22", MASTER_USER = "replication", MASTER_PASSWORD = "password_of_user_replication", MASTER_LOG_FILE = "mysql-bin.000006", MASTER_LOG_POS = 5161; с полученными на предыдущем шаге названием лога и позицией.
    — вновь привязывает слейва: mysql@ubuntu3> slave start;
    Пока реализация данного скрипта не готова, думаю на чем его писать perl/php/python/bash…
    Буду рад услышать мысли хабрасообщества на эту тему. Пишите если статья оказалась полезной, в планах всё-таки разобраться с кластеризацией и сделать репликацию кластеров.

    Похожие публикации

    Средняя зарплата в IT

    120 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 5 953 анкет, за 1-ое пол. 2021 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 12

      +1
      велосипед давно изобретен
      mysql-mmm.org/start
      и вот onlamp.com/onlamp/2006/04/20/advanced-mysql-replication.html чтоб понять принципы
        0
        Большое спасибо за ссылки, многое подчерпнул из этих статей.
        Но есть вопрос. Скрипты MMM по сути разработаны для топологии звезда с однонаправленными связями. Один мастер и несколько слейвов, при падении мастера осуществляется выбор нового мастера. При этом идет разделение запросов на чтение/запись и, как я понял все запросы на запись идут только к мастеру, а все запросы на чтение — только к слейвам (видимо чтобы разгрузить мастера). Так вот, моя же задача состоит в том, чтобы читать/писать можно было на все сервера, т.е. получается гомогенная среда. Это было в начале статьи по вашей ссылке. И при падении одного из узлов кольца, нужно замыкать кольцо на другой узел. А при восстановлении узла, включать его в кольцо. Есть ли какой-нибудь софт для этих целей, или можно как-то настроить MMM для этого?
        0
        Спасибо за статью! А не подскажете? как реализовать следующею схему и можно ли. Чую что можно…

        Пусть у нас есть ОЧЕНЬ важная база Base1 (номера кридиток допусти) которая должна работать всегда.
        И мене важная база со всем остальным Base2

        Цель — обеспечить повышенную отказоустойчивость для Base1 и приемлемую отказоустойчивость для Base2

        1) Server1 используется на запись для Base1 (то есть он мастер для Base1)
        2) Server2 является слейвом для Base1, однако на нем крутиться также Base2 и в нее пишут.
        3) Server3 является слейвом для Server2 и на него реплецируются обе базы (Base1, Base2)

        Судя по вашему посту сделать коскадную репликацию из трех серверов одной базы — можно. Но может ли один инстенс MySQL быть мастером для одной базы и слейво для второй? Или для Server2 нужно будет поднимать два экземпляра MySQL?

        Server1.Base1 (m) ---> (s) Server2.Base1 (s) --> (s) Server3.Base1
        =================== Server2.Base1 (m) --> (s) Server3.Base2

          0
          Но может ли один инстенс MySQL быть мастером для одной базы и слейвом для второй?

          На сколько я знаю, в mysql нет разделение на master-slave по базам. Предлагаю сделать так:
          server2 будет мастером для server3, но в свою очередь будет слейвом для server1.
          Будет нечто вроде этого:

          Server1.Base1 (m) ---> (s) Server2.Base1 (m) --> (s) Server3.Base1
          =================== Server2.Base1 (m) --> (s) Server3.Base2

          И вы можете спокойно писать и в server1 и в server2, не забыв добавить

          log-slave-updates

          в конфиг server2. Получите, как раз то, что требуется.
            0
            А как обстоят дела с автоинкрементными ключами? По идее должны быть конфликты-же?
              +1
              Ну собственно вот так:

              Quoting from the manual:

              * auto_increment_increment controls the increment between successive AUTO_INCREMENT values.
              * auto_increment_offset determines the starting point for AUTO_INCREMENT column values.

              By choosing non-conflicting values for these variables on different masters, servers in a multiple-master configuration will not use conflicting AUTO_INCREMENT values when inserting new rows into the same table. To set up N master servers, set the variables like this:

              * Set auto_increment_increment to N on each master.
              * Set each of the N masters to have a different auto_increment_offset, using the values 1, 2,…, N.

              Using those two variables as described in the manual, you can ensure that all nodes in your replication array will use different sequences of auto-incrementing numbers. For example, using auto_increment_increment = 10 and auto_increment_offset=3, the numbers generated when inserting three records will be 3, 13, 23. Using 10, 7, you'll get 7, 17, 27, and so on.

              Добавлю в статью.
                0
                понятно, я нечто подобное и предполагал
              0
              zizop если не затруднит свяжись со мной
              ICQ 997544

              Прочитал твои статьи, есть личный вопрос (Работа)
                0
                При падении хотя бы одного сервера — на всех остальных данные рассинхронизируются.
                Это своего рода MySQL RAID0.
                  0
                  multi-master схема оказывается обычно чертовски накладной в содержании и обслуживании. Все-таки, mysql — не кластерная субд, и такое ее использование черевато.
                  Операции записи/удаления могут вызывать конфликты, например.
                    0
                    А если у нас есть такая схема replic1(ro)replic2(ro). При падении мастера необходимо сделать одну таблицу в БД rw. Соответственно между первым и вторым репликом должна подняться связь master-slave на время падения мастера. Или есть другой способ обеспечить целостность данных на время падения мастера?
                      0
                      багает… схема: реплик1(rо)-мастер(rw)-реплик2(ro)

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

                    Самое читаемое