Этот пост был изначально написан Stephane Combaudon
Одна из особенностей MySQL 5.6, которая интересует многих, — это идентификаторы глобальных транзакций ( GTID) . Это по уважительной причине: переподключение подчиненного к новому хозяину всегда было проблемой, хотя это и так тривиально, когда включены GTID. Однако использование GTID не только заменяет старый добрый файл / позицию binlog уникальными идентификаторами, но и использует новый протокол репликации. И если вы не знаете об этом, он может укусить.
Протоколы репликации: старые против новых
Старый протокол довольно прост: ведомое устройство подключается к заданному двоичному файлу журнала с определенным смещением, и мастер отправляет все транзакции оттуда.
Новый протокол немного отличается: ведомый сначала отправляет диапазон выполненных им GTID, а затем мастер отправляет каждую пропущенную транзакцию. Это также гарантирует, что транзакция с данным GTID может быть выполнена только один раз на определенном ведомом устройстве.
На практике это что-то меняет? Ну, это может многое изменить. Представьте себе следующую ситуацию: вы хотите начать репликацию из trx 4, но trx 2 по какой-то причине отсутствует на ведомом устройстве.
Со старым протоколом репликации, 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 сервера, как на картинке ниже:
Давайте предположим, что 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, убедитесь, что вы правильно понимаете новый протокол репликации, иначе вы можете в конечном итоге нарушить репликацию новыми неожиданными способами.
Я сделаю больше исследования о ошибочных транзакциях в следующем посте.