Настройка многоканальной репликации в MySQL

Содержание

Введение

Имеется кластер Percona XtraDB Cluster, состоящий из двух узлов с Percona XtraDB Cluster Server (проще говоря - с серверами MySQL) и одного узла Percona XtraDB Cluster Galera Arbitrator (проще говоря - с абритром Galera). Кластер настроен в первую очередь для обеспечения высокой доступности сервиса - узлы кластера можно поочерёдно выводить из работы для их обслуживания без прерывания работы сервиса. Кроме того, при выходе из строя одного из серверов, сервис может продолжить работу в штатном режиме или восстановить работу в штатном режиме автоматически. В случае более сложных проблем остаётся возможность оперативно восстановить работу сервиса в ручном режиме, исключив из работы проблемный сервер.

Также для разгрузки кластера настроена репликация с одного из узлов кластера на ещё один сервер с Percona Server (или, проще говоря, с сервером MySQL), функция которого - снять нагрузку с серверов кластера. На этом сервере выполняются аналитические запросы и выполняется резервное копирование базы данных.

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

Ручная замена источника

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

STOP SLAVE;
RESET SLAVE ALL;
CHANGE MASTER TO MASTER_HOST = '192.168.122.14',
                 MASTER_USER = 'repl',
                 MASTER_PASSWORD = '$ecretP4$$w0rd',
                 MASTER_AUTO_POSITION = 1;
START SLAVE;

Настройка многоканальной репликации

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

Сначала нужно включить хранение инфорации о журналах репликации в таблицах, а не в файлах, как это принято по умолчанию. Для этого нужно остановить репликацию, поменять настройки, после чего репликацию можно возобновить:

STOP SLAVE;
SET GLOBAL master_info_repository = 'TABLE';
SET GLOBAL relay_log_info_repository = 'TABLE';
START SLAVE;

Теперь новые настройки можно прописать в файл конфигурации сервера, например, в /etc/mysql/percona-server.conf.d/mysqld.cnf:

master_info_repository = TABLE
relay_log_info_repository = TABLE

Следующим этапом можно удалить безымянный канал репликации и добавить несколько именованных каналов:

STOP SLAVE;
RESET SLAVE ALL;
CHANGE MASTER TO MASTER_HOST = '192.168.122.13',
                 MASTER_USER = 'repl',
                 MASTER_PASSWORD = '$ecretP4$$w0rd',
                 MASTER_AUTO_POSITION = 1
                 FOR CHANNEL 'pxc1';
CHANGE MASTER TO MASTER_HOST = '192.168.122.14',
                 MASTER_USER = 'repl',
                 MASTER_PASSWORD = '$ecretP4$$w0rd',
                 MASTER_AUTO_POSITION = 1
                 FOR CHANNEL 'pxc2';
START SLAVE;

Управление каналами репликации

Остановить репликацию по одному из каналов можно следующим образом:

STOP SLAVE FOR CHANNEL 'pxc1';

При необходимости удалить один из каналов, можно сначала остановить репликацию по этому каналу указанной выше командой, а затем удалить сам канал следующим образом:

RESET SLAVE ALL FOR CHANNEL 'pxc1';

Для удаления безымянного канала вместо имени нужно указать пустую строку.

Для добавления нового именованного канала предназначена уже указанная выше команда следующего вида:

CHANGE MASTER TO MASTER_HOST = '192.168.122.13',
                 MASTER_USER = 'repl',
                 MASTER_PASSWORD = '$ecretP4$$w0rd',
                 MASTER_AUTO_POSITION = 1
                 FOR CHANNEL 'pxc1';

После добавления нового канала запустить репликацию по этому каналу можно с помощью команды следующего вида:

START SLAVE FOR CHANNEL 'pxc1';

Несинхронность GTID

Если сравнить GTID в журналах репликации на обоих узлах с помощью команды mysqlbinlog, то можно обнаружить, что одна и та же транзацкия в разных жунралах получает разные GTID:

pxc1:

# at 517920750
#250131  9:59:34 server id 1  end_log_pos 517920815 CRC32 0xe8b951f2    GTID    last_committed=697058   sequence_number=697059  rbr_only=no
SET @@SESSION.GTID_NEXT= 'd4ee4813-079f-ee11-4d66-d093339ebf17:4694457010'/*!*/;
# at 517920815
#250131  9:59:34 server id 1  end_log_pos 517920962 CRC32 0xbbb46a06    Query   thread_id=13066003      exec_time=0     error_code=0
SET TIMESTAMP=1738299574/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
create table test (id int primary key auto_increment) engine=innodb
/*!*/;

...

# at 524949140
#250131  9:59:48 server id 1  end_log_pos 524949205 CRC32 0x8819319b    GTID    last_committed=705615   sequence_number=705616  rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'd4ee4813-079f-ee11-4d66-d093339ebf17:4694465567'/*!*/;
# at 524949205
#250131  9:59:48 server id 1  end_log_pos 524949285 CRC32 0xc2af333c    Query   thread_id=13066003      exec_time=0     error_code=0
SET TIMESTAMP=1738299588/*!*/;
BEGIN
/*!*/;
# at 524949285
#250131  9:59:48 server id 1  end_log_pos 524949335 CRC32 0xe32ac846    Table_map: `billing`.`test` mapped to number 549990
# at 524949335
#250131  9:59:48 server id 1  end_log_pos 524949375 CRC32 0x8c21029f    Write_rows: table id 549990 flags: STMT_END_F

BINLOG '
xFicZxMBAAAAMgAAAFcXSh8AAGZkCAAAAAEAB2JpbGxpbmcABHRlc3QAAQMAAEbIKuM=
xFicZx4BAAAAKAAAAH8XSh8AAGZkCAAAAAEAAgAB//4DAAAAnwIhjA==
'/*!*/;
# at 524949375
#250131  9:59:48 server id 1  end_log_pos 524949406 CRC32 0x351cd3eb    Xid = 20289093268
COMMIT/*!*/;

pxc2:

# at 1061098281
#250131  9:59:34 server id 1  end_log_pos 1061098346 CRC32 0x1c59ce44   GTID    last_committed=1401385  sequence_number=1401386 rbr_only=no
SET @@SESSION.GTID_NEXT= 'd4ee4813-079f-ee11-4d66-d093339ebf17:4694460808'/*!*/;
# at 1061098346
#250131  9:59:34 server id 1  end_log_pos 1061098493 CRC32 0x6a56e61e   Query   thread_id=13066003      exec_time=0     error_code=0
SET TIMESTAMP=1738299574/*!*/;
SET @@session.sql_mode=0/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
create table test (id int primary key auto_increment) engine=innodb
/*!*/;

...

# at 1068014504
#250131  9:59:48 server id 1  end_log_pos 1068014569 CRC32 0xcad9d196   GTID    last_committed=1409942  sequence_number=1409943 rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'd4ee4813-079f-ee11-4d66-d093339ebf17:4694469365'/*!*/;
# at 1068014569
#250131  9:59:48 server id 1  end_log_pos 1068014637 CRC32 0x39de9c31   Query   thread_id=13066003      exec_time=0     error_code=0
SET TIMESTAMP=1738299588/*!*/;
BEGIN
/*!*/;
# at 1068014637
#250131  9:59:48 server id 1  end_log_pos 1068014687 CRC32 0xe29c84d2   Table_map: `billing`.`test` mapped to number 107398
# at 1068014687
#250131  9:59:48 server id 1  end_log_pos 1068014727 CRC32 0xd144e6bc   Write_rows: table id 107398 flags: STMT_END_F

BINLOG '
xFicZxMBAAAAMgAAAF+cqD8AAIajAQAAAAEAB2JpbGxpbmcABHRlc3QAAQMAANKEnOI=
xFicZx4BAAAAKAAAAIecqD8AAIajAQAAAAEAAgAB//4DAAAAvOZE0Q==
'/*!*/;
# at 1068014727
#250131  9:59:48 server id 1  end_log_pos 1068014758 CRC32 0xc802017d   Xid = 20289093268
COMMIT/*!*/;

Таким образом, сменить источник репликации оказывается невозможно без последствий для исправности репликации.