Настройка многоканальной репликации в 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/*!*/;
Таким образом, сменить источник репликации оказывается невозможно без последствий для исправности репликации.