MySQL InnoDB Cluster: групповая репликация и отказоустойчивость

MySQL InnoDB Cluster: архитектура и компоненты

MySQL InnoDB Cluster — это встроенное решение Oracle для высокой доступности MySQL, объединяющее три технологии:

  • Group Replication — синхронная мульти-мастер репликация на уровне InnoDB
  • MySQL Shell — административная утилита для управления кластером
  • MySQL Router — прокси для автоматического failover и балансировки

В отличие от классической master-slave репликации, Group Replication обеспечивает:

  • Автоматический выбор нового Primary при сбое
  • Консенсус на основе протокола Paxos — данные коммитятся только после подтверждения большинством нод
  • Обнаружение конфликтов при одновременной записи в multi-primary режиме
  • Автоматическое восстановление отставших нод через distributed recovery

Минимальная конфигурация — 3 ноды MySQL Server. Рекомендуется нечётное число для обеспечения кворума. Максимум — 9 нод.

В данной статье мы развернём кластер из 3 нод на Ubuntu 22.04 с MySQL 8.0 в режиме single-primary (одна нода принимает запись, остальные — только чтение).

Подготовка серверов MySQL

Устанавливаем MySQL 8.0 на все три ноды:

sudo apt update
sudo apt install mysql-server mysql-shell mysql-router -y

На каждой ноде настраиваем /etc/mysql/mysql.conf.d/mysqld.cnf. Конфигурация для ноды 1 (10.0.1.11):

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

# Обязательно для Group Replication
gtid_mode = ON
enforce_gtid_consistency = ON
binlog_checksum = NONE
log_bin = binlog
log_slave_updates = ON
binlog_format = ROW
master_info_repository = TABLE
relay_log_info_repository = TABLE
transaction_write_set_extraction = XXHASH64

# Group Replication
plugin_load_add = 'group_replication.so'
group_replication_group_name = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
group_replication_start_on_boot = OFF
group_replication_local_address = "10.0.1.11:33061"
group_replication_group_seeds = "10.0.1.11:33061,10.0.1.12:33061,10.0.1.13:33061"
group_replication_bootstrap_group = OFF

# Производительность
innodb_buffer_pool_size = 4G
innodb_log_file_size = 1G
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1

# Слушать на всех интерфейсах
bind-address = 0.0.0.0
report_host = 10.0.1.11

На нодах 2 и 3 измените server-id, group_replication_local_address и report_host на соответствующие IP-адреса.

Перезапускаем MySQL на всех нодах:

sudo systemctl restart mysql

Создание пользователя для репликации

На каждой ноде создаём пользователя для InnoDB Cluster:

mysql -u root -p

CREATE USER 'cluster_admin'@'%' IDENTIFIED BY 'ClusterP@ss2026!';
GRANT ALL PRIVILEGES ON *.* TO 'cluster_admin'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

-- Создаём пользователя для MySQL Router
CREATE USER 'router_user'@'%' IDENTIFIED BY 'R0uterP@ss!';
GRANT ALL PRIVILEGES ON mysql_innodb_cluster_metadata.* TO 'router_user'@'%';
FLUSH PRIVILEGES;

Создание кластера через MySQL Shell

MySQL Shell — основной инструмент для управления InnoDB Cluster. Подключаемся к первой ноде:

mysqlsh cluster_admin@10.0.1.11

Проверяем готовность ноды:

dba.checkInstanceConfiguration('cluster_admin@10.0.1.11')
dba.checkInstanceConfiguration('cluster_admin@10.0.1.12')
dba.checkInstanceConfiguration('cluster_admin@10.0.1.13')

Если обнаружены проблемы, исправляем автоматически:

dba.configureInstance('cluster_admin@10.0.1.11')
dba.configureInstance('cluster_admin@10.0.1.12')
dba.configureInstance('cluster_admin@10.0.1.13')

Создаём кластер на первой ноде:

# Подключаемся к первой ноде
\connect cluster_admin@10.0.1.11

# Создаём кластер
var cluster = dba.createCluster('ProductionCluster', {
    memberWeight: 50,
    exitStateAction: 'READ_ONLY',
    consistency: 'BEFORE_ON_PRIMARY_FAILOVER'
})

Добавляем остальные ноды:

cluster.addInstance('cluster_admin@10.0.1.12', {
    recoveryMethod: 'clone'
})

cluster.addInstance('cluster_admin@10.0.1.13', {
    recoveryMethod: 'clone'
})

Параметр recoveryMethod: 'clone' использует MySQL Clone Plugin для полного копирования данных с Primary на новую ноду — это быстрее, чем инкрементальное восстановление из бинлогов.

Проверяем статус кластера:

cluster.status()

// Ожидаемый вывод:
// "status": "OK",
// "topology": {
//   "10.0.1.11:3306": { "mode": "R/W", "status": "ONLINE" },
//   "10.0.1.12:3306": { "mode": "R/O", "status": "ONLINE" },
//   "10.0.1.13:3306": { "mode": "R/O", "status": "ONLINE" }
// }

Настройка MySQL Router для автоматического failover

MySQL Router — прокси-сервер, который направляет запросы на запись к Primary-ноде, а запросы на чтение балансирует между Secondary. При failover Router автоматически переключается на нового Primary.

Инициализация Router (выполняется на сервере приложения или отдельном сервере):

mysqlrouter --bootstrap cluster_admin@10.0.1.11:3306 \
  --directory /etc/mysqlrouter \
  --conf-use-sockets \
  --account router_user \
  --user mysqlrouter

Router создаёт конфигурацию с несколькими портами:

  • 6446 — R/W (запись) → всегда Primary
  • 6447 — R/O (чтение) → балансировка между Secondary
  • 6448 — R/W через X Protocol
  • 6449 — R/O через X Protocol

Запускаем Router:

sudo systemctl enable --now mysqlrouter

Конфигурация приложения: вместо прямого подключения к MySQL используйте Router:

# Вместо mysql://10.0.1.11:3306/mydb
# Запись:
mysql://router-host:6446/mydb
# Чтение:
mysql://router-host:6447/mydb

Пример конфигурации для PHP-приложения:

// Запись (INSERT, UPDATE, DELETE)
$write_db = new PDO('mysql:host=router.company.local;port=6446;dbname=myapp', 'appuser', 'AppP@ss');

// Чтение (SELECT)
$read_db = new PDO('mysql:host=router.company.local;port=6447;dbname=myapp', 'appuser', 'AppP@ss');

Тестирование failover

Проверим, что кластер корректно переживает отказ Primary-ноды.

# Определяем текущий Primary
mysqlsh cluster_admin@10.0.1.11 -- cluster status

# Останавливаем MySQL на Primary
sudo systemctl stop mysql   # на ноде 10.0.1.11

# Проверяем статус кластера (подключаемся к другой ноде)
mysqlsh cluster_admin@10.0.1.12 -- cluster status

# Ожидаемый результат: один из Secondary стал новым Primary
# 10.0.1.12 → R/W (новый Primary)
# 10.0.1.13 → R/O
# 10.0.1.11 → OFFLINE

Восстановление ноды после сбоя:

# Запускаем MySQL на восстановленной ноде
sudo systemctl start mysql   # на 10.0.1.11

# Нода автоматически присоединяется к кластеру как Secondary
mysqlsh cluster_admin@10.0.1.12 -- cluster status
# 10.0.1.11 → R/O (восстановлена)

Ручное переключение Primary (switchover):

mysqlsh cluster_admin@10.0.1.12
var cluster = dba.getCluster()
cluster.setPrimaryInstance('cluster_admin@10.0.1.11')

Это полезно при плановом обслуживании — переключаете Primary на другую ноду, обновляете сервер и возвращаете его в кластер.

Мониторинг кластера

MySQL Shell предоставляет подробную информацию о состоянии кластера:

mysqlsh cluster_admin@10.0.1.11
var cluster = dba.getCluster()

# Статус кластера
cluster.status({extended: 1})

# Подробности по каждой ноде
cluster.describe()

# Проверка конфигурации
cluster.checkInstanceState('cluster_admin@10.0.1.13')

SQL-запросы для мониторинга Group Replication:

-- Статус всех членов группы
SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE, MEMBER_ROLE
FROM performance_schema.replication_group_members;

-- Статистика применения транзакций
SELECT * FROM performance_schema.replication_group_member_stats\G

-- Задержка репликации
SELECT
  MEMBER_HOST,
  COUNT_TRANSACTIONS_IN_QUEUE as queue,
  COUNT_TRANSACTIONS_CHECKED as checked,
  COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE as applier_queue
FROM performance_schema.replication_group_member_stats;

Для интеграции с Prometheus используйте mysqld_exporter с метриками Group Replication:

# Запуск mysqld_exporter
mysqld_exporter --web.listen-address=:9104 \
  --collect.info_schema.innodb_metrics \
  --collect.perf_schema.replication_group_members \
  --collect.perf_schema.replication_group_member_stats

Типичные проблемы и их решение

При эксплуатации InnoDB Cluster могут возникать характерные ситуации:

Нода не может присоединиться к кластеру (RECOVERING):

# Проверяем ошибки
SELECT * FROM performance_schema.replication_connection_status\G

# Если GTIDs разошлись — полная реинициализация
mysqlsh cluster_admin@primary
var cluster = dba.getCluster()
cluster.removeInstance('cluster_admin@problem-node', {force: true})
cluster.addInstance('cluster_admin@problem-node', {recoveryMethod: 'clone'})

Split-brain (потеря кворума):

# Если осталась только одна нода
mysqlsh cluster_admin@surviving-node
var cluster = dba.getCluster()

# Принудительное восстановление кворума
cluster.forceQuorumUsingPartitionOf('cluster_admin@surviving-node')

Медленная работа после failover:

  • Проверьте, что приложения подключены через Router, а не напрямую к нодам
  • Убедитесь в настройке consistency: 'BEFORE_ON_PRIMARY_FAILOVER' — это гарантирует, что новый Primary применит все pending-транзакции перед приёмом запросов

Ошибка «This member has more executed transactions than those present in the group»:

# Нода имеет транзакции, которых нет в кластере
# Решение: очистить и переинициализировать
RESET MASTER;
# Затем заново добавить в кластер с recoveryMethod: 'clone'

Часто задаваемые вопросы

InnoDB Cluster использует встроенную Group Replication (протокол Paxos), Galera — сертификационную репликацию (wsrep API). InnoDB Cluster поддерживается Oracle, интегрирован с MySQL Shell и Router. Galera (Percona XtraDB Cluster, MariaDB Galera) — сторонняя технология с более зрелым multi-primary режимом. Для MySQL 8.0 InnoDB Cluster — рекомендуемое Oracle решение.

Single-primary подходит для большинства случаев: одна нода принимает запись, остальные — только чтение. Multi-primary позволяет писать на любую ноду, но возможны конфликты при одновременном обновлении одних и тех же строк. Multi-primary рекомендуется только для приложений с чётко разделёнными потоками записи.

Group Replication чувствительна к задержке сети. Для WAN-репликации между дата-центрами используйте асинхронный replication channel от кластера к удалённому серверу. InnoDB ClusterSet (MySQL 8.0.27+) предоставляет нативную поддержку multi-datacenter с автоматическим DR failover.

Используйте rolling upgrade: обновляйте по одной ноде, начиная с Secondary. Остановите MySQL, обновите пакеты, запустите — нода автоматически присоединится к кластеру. После обновления всех Secondary переключите Primary на обновлённую ноду (setPrimaryInstance) и обновите последнюю. Кластер остаётся доступным на протяжении всего процесса.

Нужна помощь с настройкой?

Специалисты АйТи Фреш помогут с внедрением и настройкой — 15+ лет опыта, обслуживание от 15 000 ₽/мес

📞 Связаться с нами
#MySQL InnoDB Cluster#Group Replication#MySQL кластер#отказоустойчивость MySQL#MySQL Shell#MySQL Router#репликация MySQL#высокая доступность
Комментарии 0

Оставить комментарий

загрузка...