6.5.10 Настройка репликации MySQL

TheHost replication Banner ru

MySQL-репликация это механизм, который автоматически синхронизирует данные между серверами в режиме реального времени. Она решает сразу три задачи: если основной сервер упадёт, данные не потеряются; нагрузку на чтение можно распределить между несколькими серверами; наконец, резервные копии можно снимать без остановки основной базы. В этом гайде рассматриваются два подхода к репликации. Первый — классический source-replica (он же master-slave): один главный сервер принимает все изменения, остальные их копируют. Второй — двусторонняя master-master репликация, где каждый сервер может одновременно и читать, и писать. Все примеры и команды актуальны для MySQL 8.0.23+, где Oracle переименовала устаревшие термины — там, где это важно, старый синтаксис отмечен отдельно.

Что нужно подготовить перед началом

Прежде чем трогать конфигурацию MySQL, убедитесь, что инфраструктура готова. Все последующие шаги предполагают, что у вас есть два отдельных сервера (физических, виртуальных или контейнера) с MySQL 8.0+ одинаковой версии.

Требования

  • Два отдельных сервера — физических, виртуальных или контейнера
  • MySQL 8.0+ одинаковой версии на обоих серверах
  • Открытый порт 3306 открыт между серверами (проверьте: telnet <ip_второго_сервера> 3306)

Какой тип репликации лучше выбрать

Source-Replica (master-slave) — лучший выбор для новичков и большинства production-сценариев. Подходит для масштабирования чтения (read replicas), создания резервных копий без нагрузки на production-сервер, аналитических запросов на отдельном сервере и простого failover (ручное повышение реплики до source). Сложность настройки низкая, риск потери данных минимальный при sync_binlog=1.

Master-Master (двусторонняя) — подходит только когда нужна запись на два узла в разных локациях или мгновенный failover без промоушна реплики. На практике большинство команд используют master-master в режиме active-passive: пишут только в один узел, второй держат как hot standby. Сложность высокая, риск конфликтов реальный при одновременной записи.

Group Replication / InnoDB Cluster — рекомендованный Oracle вариант для true multi-master. Имеет встроенный механизм разрешения конфликтов (certification-based), автоматический failover и quorum. Требует минимум 3 узла. Если нужен надёжный multi-master — это правильный выбор вместо ручного master-master.

Обратите внимание! В данной статье мы будем использовать следующие ip-адреса в качестве примера.

  • source - 192.168.0.10
  • slave - 192.168.0.20

Вам же нужно будет указать Ваши ip-адреса.

TheHost Replication master-slave

Source-Replica (master-slave) репликация

Это наиболее простой и распространённый тип репликации MySQL. Один сервер (source) принимает все операции записи, второй (replica) автоматически получает и применяет изменения. Реплика обычно работает как read-only узел и используется для разгрузки чтения, резервирования и повышения отказоустойчивости.

Если вы только начинаете работать с репликацией, лучше сразу использовать схему source-replica вместе с GTID (Global Transaction Identifier).

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

3E11FA47-71CA-11E1-9E33-C80AA9429562:23

Где:

  • 3E11FA47-71CA-11E1-9E33-C80AA9429562 — UUID сервера, на котором транзакция была создана;
  • 23 — порядковый номер транзакции.

Почему лучше использовать GTID

Традиционный способ настройки репликации использует координаты бинарного лога:

  • SOURCE_LOG_FILE
  • SOURCE_LOG_POS

Это рабочий вариант, но он менее удобен:

  • нужно вручную фиксировать файл и позицию;
  • выше риск ошибки при первичной настройке;
  • сложнее восстанавливать репликацию после сбоя;
  • неудобнее выполнять переключение на другой source.

С GTID репликация становится проще и надёжнее:

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

Конфигурация my.cnf на source-сервере

Файл: /etc/mysql/mysql.conf.d/mysqld.cnf (Ubuntu/Debian) или /etc/my.cnf (RHEL/CentOS).

[mysqld]
# Уникальный идентификатор сервера
server-id                       = 1

# Включение бинарного лога
log_bin                         = /var/log/mysql/mysql-bin.log

# Рекомендуемый формат бинлога
binlog_format                   = ROW

# Включение GTID
gtid_mode                       = ON
enforce_gtid_consistency        = ON

# Разрешить подключения извне
bind-address                    = 0.0.0.0

# Безопасная запись бинлога на диск
sync_binlog                     = 1

# Срок хранения бинарных логов
binlog_expire_logs_seconds      = 604800

# Максимальный размер файла бинлога
max_binlog_size                 = 100M

# (Опционально) Реплицировать только конкретные базы:
# binlog_do_db                  = mydb
# binlog_do_db                  = another_db

Конфигурация my.cnf на replica-сервере

[mysqld]
# Уникальный идентификатор сервера, отличный от source
server-id                       = 2

# Relay log для хранения событий от source
relay-log                       = /var/log/mysql/mysql-relay-bin.log

# Защита от случайных записей на реплике
read_only                       = ON
super_read_only                 = ON

# Бинарный лог на реплике
log_bin                         = /var/log/mysql/mysql-bin.log

# Записывать реплицированные события в бинлог реплики
log_replica_updates             = ON

# Включение GTID
gtid_mode                       = ON
enforce_gtid_consistency        = ON

После редактирования конфигурации на обоих серверах перезапустите MySQL:

sudo systemctl restart mysql

Настройка репликации с GTID

Шаг 1 — Создать пользователя для репликации на source

На source-сервере нужно создать отдельного пользователя, через которого replica будет подключаться и читать изменения из бинарного лога. Для этого пользователю выдаются права репликации. Рекомендуется ограничить доступ по IP-адресу replica-сервера, а не разрешать подключение с любого хоста.

-- Вариант A: caching_sha2_password (рекомендуется для MySQL 8.0)
CREATE USER 'repl_user'@'192.168.0.20' IDENTIFIED BY 'StrongP@ss123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'192.168.0.20';
FLUSH PRIVILEGES;

Альтернативно, если требуется совместимость со старой конфигурацией:

-- Вариант B: mysql_native_password
CREATE USER 'repl_user'@'192.168.0.20'
IDENTIFIED WITH mysql_native_password BY 'StrongP@ss123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'192.168.0.20';
FLUSH PRIVILEGES;

Если используется caching_sha2_password без SSL, на replica потребуется указать GET_SOURCE_PUBLIC_KEY=1 в команде CHANGE REPLICATION SOURCE TO.

Шаг 2 — Подготовить консистентный снимок базы на source

Перед созданием дампа нужно зафиксировать состояние данных, чтобы snapshot получился согласованным. Это особенно важно, если на source в этот момент продолжаются изменения. Блокировка позволяет избежать ситуации, когда часть данных попала в дамп до изменения, а часть — уже после него.

Для получения корректного дампа выполните:

FLUSH TABLES WITH READ LOCK;

Внимание! Не закрывайте эту сессию до завершения создания дампа. Если соединение оборвётся, блокировка снимется.

Шаг 3 — Сделать дамп базы

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

В отдельном терминале выполните:

mysqldump -u root -p \
  --databases mydb \
  --single-transaction \
  --routines \
  --triggers \
  --events \
  > /tmp/source_dump.sql

Пояснения:

  • --single-transaction даёт консистентный снимок для InnoDB-таблиц;
  • --routines, --triggers, --events включают связанные объекты;
  • при использовании GTID нет необходимости опираться на SOURCE_LOG_FILE и SOURCE_LOG_POS как на основной механизм настройки.

После завершения дампа снимите блокировку:

UNLOCK TABLES;

Передайте дамп на replica:

scp /tmp/source_dump.sql user@192.168.0.20:/tmp/

Шаг 4 — Импортировать дамп на replica

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

mysql -u root -p < /tmp/source_dump.sql

Затем подключитесь к MySQL на replica:

mysql -u root -p

Шаг 5 — Настроить репликацию через GTID на replica

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

Выполните:

CHANGE REPLICATION SOURCE TO
    SOURCE_HOST='192.168.0.10',
    SOURCE_PORT=3306,
    SOURCE_USER='repl_user',
    SOURCE_PASSWORD='StrongP@ss123!',
    SOURCE_AUTO_POSITION=1,
    GET_SOURCE_PUBLIC_KEY=1;

Пояснения:

  • SOURCE_AUTO_POSITION=1 включает репликацию через GTID;
  • в этом режиме MySQL автоматически определяет, какие транзакции уже применены на replica;
  • GET_SOURCE_PUBLIC_KEY=1 нужен при использовании caching_sha2_password без SSL.

Шаг 6 — Запустить и проверить репликацию

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

START REPLICA;
SHOW REPLICA STATUS\G

Ключевые поля для проверки:

Replica_IO_Running:  Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 0
Last_IO_Error:
Last_SQL_Error:

Что это означает:

  • Replica_IO_Running: Yes — replica подключилась к source и читает бинарный лог;
  • Replica_SQL_Running: Yes — replica применяет полученные изменения;
  • Seconds_Behind_Source: 0 — отставание отсутствует;
  • Last_IO_Error и Last_SQL_Error должны быть пустыми.

Если оба потока работают и ошибок нет, репликация настроена корректно. TheHost replication master-master

Двусторонняя (master-master) репликация

Двусторонняя репликация — это два сервера, каждый из которых одновременно является source и replica для другого. Изменения на Node1 реплицируются на Node2, и наоборот. Это существенно сложнее обычной source-replica, главная причина — риск конфликтов при одновременной записи на оба узла. MySQL не имеет встроенного механизма разрешения конфликтов для стандартной InnoDB-репликации.

Что меняется по сравнению с master-slave

Основных отличий пять. Во-первых, оба узла требуют включённый log_bin, поскольку каждый выступает source. Во-вторых, log_replica_updates должен быть ON на обоих узлах — без этого реплицированные события не попадают в локальный бинлог, и circular replication не работает. В-третьих, auto_increment_increment и auto_increment_offset необходимы для избежания коллизий первичных ключей. В-четвёртых, оба узла требуют пользователя для репликации. В-пятых, оба узла являются writable (не read-only).

Конфигурация my.cnf для двух узлов

Node 1 (IP: 192.168.1.10):

[mysqld]
server-id                = 1
log_bin                  = /var/log/mysql/mysql-bin.log
binlog_format            = ROW
relay_log                = /var/log/mysql/mysql-relay-bin
log_replica_updates      = ON
bind-address             = 0.0.0.0

# Критично: разделение AUTO_INCREMENT между узлами
auto_increment_increment = 2
auto_increment_offset    = 1
# Node 1 генерирует ID: 1, 3, 5, 7, 9...

binlog_expire_logs_seconds = 604800
max_binlog_size          = 100M

Node 2 (IP: 192.168.1.20):

[mysqld]
server-id                = 2
log_bin                  = /var/log/mysql/mysql-bin.log
binlog_format            = ROW
relay_log                = /var/log/mysql/mysql-relay-bin
log_replica_updates      = ON
bind-address             = 0.0.0.0

# Node 2 генерирует ID: 2, 4, 6, 8, 10...
auto_increment_increment = 2
auto_increment_offset    = 2

binlog_expire_logs_seconds = 604800
max_binlog_size          = 100M

Ключевая переменная — auto_increment_increment=2 с разным auto_increment_offset на каждом узле. Это гарантирует, что Node 1 генерирует нечётные AUTO_INCREMENT-значения (1, 3, 5…), а Node 2 — чётные (2, 4, 6…), полностью избегая конфликтов первичных ключей. Для N узлов используйте auto_increment_increment=N с offset от 1 до N.

Переменная log_replica_updates (или log_slave_updates до MySQL 8.0.26) обеспечивает, что события, полученные от партнёра, записываются в собственный бинлог. Без неё circular replication невозможна — Node1 отправит изменение на Node2, Node2 применит, но не запишет в свой бинлог, поэтому Node1 никогда не «увидит» обратную репликацию. MySQL предотвращает бесконечные циклы благодаря server-id: каждый узел автоматически игнорирует события со своим собственным server-id.

Пошаговая настройка circular replication

После изменения конфигурации перезапустите MySQL на обоих узлах: sudo systemctl restart mysql.

Шаг 1 — Создать пользователей для репликации на обоих узлах:

На Node 1:

CREATE USER 'repl_user'@'192.168.1.20' IDENTIFIED BY 'StrongP@ss123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'192.168.1.20';
FLUSH PRIVILEGES;

На Node 2:

CREATE USER 'repl_user'@'192.168.1.10' IDENTIFIED BY 'StrongP@ss123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'192.168.1.10';
FLUSH PRIVILEGES;

Шаг 2 — Настроить Node2 как реплику Node1:

На Node 1 выполните SHOW MASTER STATUS; и запишите File/Position.

На Node 2:

CHANGE REPLICATION SOURCE TO
    SOURCE_HOST='192.168.1.10',
    SOURCE_PORT=3306,
    SOURCE_USER='repl_user',
    SOURCE_PASSWORD='StrongP@ss123!',
    SOURCE_LOG_FILE='mysql-bin.000001',
    SOURCE_LOG_POS=<позиция_из_node1>,
    GET_SOURCE_PUBLIC_KEY=1;

START REPLICA;

Шаг 3 — Настроить Node1 как реплику Node2:

На Node 2 выполните SHOW MASTER STATUS; и запишите File/Position.

На Node 1:

CHANGE REPLICATION SOURCE TO
    SOURCE_HOST='192.168.1.20',
    SOURCE_PORT=3306,
    SOURCE_USER='repl_user',
    SOURCE_PASSWORD='StrongP@ss123!',
    SOURCE_LOG_FILE='mysql-bin.000001',
    SOURCE_LOG_POS=<позиция_из_node2>,
    GET_SOURCE_PUBLIC_KEY=1;

START REPLICA;

Шаг 4 — Проверить репликацию на ОБОИХ узлах:

SHOW REPLICA STATUS\G

На каждом узле Replica_IO_Running и Replica_SQL_Running должны показывать Yes. Протестируйте: вставьте строку на Node1 (получит нечётный ID), затем на Node2 (получит чётный ID), убедитесь, что обе строки видны на обоих узлах.

Конфликты и как их избегать

MySQL не имеет встроенного разрешения конфликтов для стандартной InnoDB master-master репликации. Если оба узла одновременно изменят одну и ту же строку, репликация сломается с ошибкой 1062 (duplicate key) или 1032 (record not found). Наиболее безопасные стратегии для избежания конфликтов: направлять все записи только на один узел (второй как hot standby), разделять запись на уровне приложения (разные таблицы или диапазоны данных на разные узлы), или использовать UUID вместо AUTO_INCREMENT для первичных ключей.

Для полноценного multi-master с автоматическим разрешением конфликтов Oracle рекомендует MySQL Group Replication (доступна с MySQL 5.7.17, значительно улучшена в 8.0) или InnoDB Cluster, который объединяет Group Replication с MySQL Router и MySQL Shell для простого управления.

Распространённые ошибки
Ошибка Причина Решение
Replica_IO_Running: No Неверные учётные данные, firewall или неправильный bind-address Проверьте пользователя: SELECT user, host FROM mysql.user WHERE user='repl_user';. Убедитесь, что bind-address в my.cnf на source равен 0.0.0.0, а не 127.0.0.1. Проверьте подключение: telnet <source_ip> 3306
Authentication plugin 'caching_sha2_password' reported error Реплика не может получить ключ аутентификации без SSL Добавьте GET_SOURCE_PUBLIC_KEY=1 в CHANGE REPLICATION SOURCE TO или переведите пользователя: ALTER USER 'repl_user'@'<replica_ip>' IDENTIFIED WITH mysql_native_password BY 'password';
Replica_SQL_Running: No — Error 1062 (Duplicate entry) Строка с таким ключом уже существует на реплике — кто-то вручную писал в реплику Удалите конфликтную строку на реплике или пропустите ошибку через SET GLOBAL sql_slave_skip_counter = 1
Replica_SQL_Running: No — Error 1032 (Can’t find record) Событие пытается обновить или удалить строку, которой нет на реплике Вставьте отсутствующую строку вручную или пропустите ошибку
Error 1236 (Binary log error) Бинарные логи на source удалены раньше, чем реплика успела их прочитать Только полная ресинхронизация: новый дамп с source и повторная настройка репликации с нуля
Одинаковый server_uuid на обоих узлах VM или Docker-контейнер был склонирован вместе с auto.cnf Остановите MySQL, удалите /var/lib/mysql/auto.cnf, запустите MySQL — новый UUID сгенерируется автоматически
Seconds_Behind_Source постоянно растёт Реплика не успевает применять изменения Проверьте нагрузку на source, железо реплики и наличие крупных транзакций; реализуйте heartbeat-таблицу для точного мониторинга лага

Заключение

Репликация MySQL 8.0 стала значительно удобнее благодаря обновлённой терминологии, GTID по умолчанию и бинарному логу, включённому «из коробки». Главное правило для новичков — начинайте с простой source-replica с GTID, всегда ставьте read_only=ON на реплике, используйте GET_SOURCE_PUBLIC_KEY=1 для caching_sha2_password, и держите binlog_expire_logs_seconds достаточно высоким, чтобы реплика успевала прочитать все логи. Master-master следует рассматривать только как active-passive failover, а для настоящего multi-master — переходить на Group Replication. И самое важное: если вы клонировали VM — всегда удаляйте /var/lib/mysql/auto.cnf перед запуском репликации.