Статьи

GTID в MySQL 5.6: новый протокол репликации, новые способы прерывания репликации

Этот пост был изначально написан Stephane Combaudon

Одна из особенностей MySQL 5.6, которая интересует многих, — это идентификаторы глобальных транзакций ( GTID) . Это по уважительной причине: переподключение подчиненного к новому хозяину всегда было проблемой, хотя это и так тривиально, когда включены GTID. Однако использование GTID не только заменяет старый добрый файл / позицию binlog уникальными идентификаторами, но и использует новый протокол репликации. И если вы не знаете об этом, он может укусить.

Протоколы репликации: старые против новых

Старый протокол довольно прост: ведомое устройство подключается к заданному двоичному файлу журнала с определенным смещением, и мастер отправляет все транзакции оттуда.

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

На практике это что-то меняет? Ну, это может многое изменить. Представьте себе следующую ситуацию: вы хотите начать репликацию из trx 4, но trx 2 по какой-то причине отсутствует на ведомом устройстве.

new_protocol

Со старым протоколом репликации, TRX-никогда не будет выполняться , а с новым протоколом репликации, это БУДЕТ выполняться автоматически.

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

Пропуск транзакций

Хорошо известно, что старый добрый SET GLOBAL sql_slave_skip_counter = Nбольше не поддерживается, когда вы хотите пропустить транзакцию и включены GTID. Вместо этого, чтобы пропустить транзакцию GTID XXX:N, вы должны ввести пустую транзакцию :

mysql> SET gtid_next = 'XXX:N';
mysql> BEGIN; COMMIT;
mysql> SET gtid_next = 'AUTOMATIC';

Почему мы не можем использовать sql_slave_skip_counter? Из-за нового протокола репликации!

Представьте, что у нас есть 3 сервера, как на картинке ниже:

new_protocol2

Давайте предположим, что sql_slave_skip_counterэто разрешено и использовалось на S2 для пропуска trx 2. Что произойдет, если вы сделаете S2 рабом S1?

Оба сервера будут обмениваться диапазоном выполненных GTID, и S1 поймет, что ему нужно отправить trx 2 на S2. Два варианта тогда:

  • Если trx 2 все еще находится в двоичных журналах S1, он будет отправлен на S2, и транзакция больше не будет пропущена.
  • Если trx 2 больше не существует в двоичных журналах S1, вы получите ошибку репликации.

Это явно не безопасно, поэтому sql_slave_skip_counterне допускается с GTID. Единственный безопасный способ пропустить транзакцию — выполнить поддельную транзакцию вместо реальной.

Ошибочные транзакции

Если вы выполняете транзакцию локально на ведомом устройстве (называемую ошибочной транзакцией в документации MySQL), что произойдет, если вы сделаете это ведомое устройство новым ведущим?

При старом протоколе репликации практически ничего нет (если быть точным, данные будут несовместимы между новым ведущим и его подчиненными, но это, вероятно, можно будет исправить позже).

С новым протоколом ошибочная транзакция будет идентифицирована как отсутствующая везде и будет автоматически выполняться при отработке отказа, что может привести к нарушению репликации.

Допустим, у вас есть ведущий (M) и 2 рабов (S1 и S2). Вот 2 простых сценария, в которых переподключение подчиненных к новому главному серверу завершится неудачно (с разными ошибками репликации):

# Сценарий 1

# S1
mysql> CREATE DATABASE mydb;
# M
mysql> CREATE DATABASE IF NOT EXISTS mydb;
# Thanks to 'IF NOT EXITS', replication doesn't break on S1. Now move S2 to S1:
# S2
mysql> STOP SLAVE; CHANGE MASTER TO MASTER_HOST='S1'; START SLAVE;
# This creates a conflict with existing data!
mysql> SHOW SLAVE STATUS\G
[...]
Last_SQL_Errno: 1007
               Last_SQL_Error: Error 'Can't create database 'mydb'; database exists' on query. Default database: 'mydb'. Query: 'CREATE DATABASE mydb'
[...]

# Сценарий 2

# S1
mysql> CREATE DATABASE mydb;
# Now, we'll remove this transaction from the binary logs
# S1
mysql> FLUSH LOGS;
mysql> PURGE BINARY LOGS TO 'mysql-bin.000008';
# M
mysql> CREATE DATABASE IF NOT EXISTS mydb;
# S2
mysql> STOP SLAVE; CHANGE MASTER TO MASTER_HOST='S1'; START SLAVE;
# The missing transaction is no longer available in the master's binary logs!
mysql> SHOW SLAVE STATUS\G
[...]
Last_IO_Errno: 1236
                Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.'
[...]

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

mysql> SET SQL_LOG_BIN = 0;
mysql> # Run local transaction

Вывод

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

Я сделаю больше исследования о ошибочных транзакциях в следующем посте.