Оглавление
- GTID простыми словами и чем он отличается от «позиционной» репликации
- Почему GTID обычно выигрывает в failover
- Подготовка к аварийному переключению
- Базовые требования GTID и репликационной цепочки
- Формат бинлога и типичные «грабли»
- Read-only дисциплина: чтобы «случайные записи» не убили GTID
- Таймауты и наблюдаемость, чтобы не «проспать» фэйл
- Аварийное переключение: четыре сценария с командами и проверками
- Сценарий: мастер недоступен, реплики синхронизированы
- Сценарий: мастер недоступен, реплики расхождены
- Сценарий: split-brain или двойной мастер
- Сценарий: восстановление старого мастера и возврат роли
- Проверка целостности данных и работа с relay log и «незавершенными» транзакциями
- Best practices: как сделать failover «скучным»
- Автоматизация: mysqlrpladmin/mysqlfailover, Orchestrator, MHA
- Шаблон bash-скрипта для промоута и переподключения реплик
- Главные риски и как их снижать
Введение
Когда мастер MySQL внезапно «умирает», главная задача DBA обычно не в том, чтобы восстановить железо, а в том, чтобы быстро и безопасно вернуть запись — не устроив двойного мастера и не потеряв больше данных, чем неизбежно при async‑репликации. GTID‑репликация сильно упрощает это упражнение: вместо плясок с master_log_file и master_log_pos вы опираетесь на факт «какие транзакции уже применены» и включаете авто‑позиционирование (MASTER_AUTO_POSITION=1). В результате переключение сводится к выбору самой актуальной реплики, её промоуту в мастер и переподключению остальных по GTID — с понятными проверками по gtid_executed/gtid_purged и статусам репликации.
Статья написана в практичном формате (с командами и чек‑листами) и с редакционными требованиями к «живому» экспертному стилю.

GTID простыми словами и чем он отличается от «позиционной» репликации
Традиционная асинхронная репликация MySQL ориентируется на бинарные логи: реплика читает события из binlog мастера, а при ручном переключении вам часто нужно знать имя файла binlog и позицию (координаты). MySQL прямо отмечает, что этот «традиционный» метод требует синхронизации файлов/позиций, тогда как подход на GTID не требует работы с лог‑файлами и позициями, заметно упрощая типовые операции администрирования и failover.
GTID (Global Transaction Identifier) — это уникальный идентификатор транзакции (на практике — UUID источника + номер/диапазон), который позволяет однозначно сказать: «вот этот набор событий — это одна транзакция». MySQL использует GTID‑сеты (множества GTID), которые отражаются в переменных gtid_executed и gtid_purged.
Два ключевых эффекта GTID для аварийного переключения:
- Авто‑позиционирование (auto‑positioning): при
MASTER_AUTO_POSITION=1реплика в handshake отправляет мастеру GTID‑сет уже полученных/выполненных транзакций, а мастер отдает только недостающие. - Идемпотентность применения: сервер автоматически пропускает транзакции с идентификаторами, которые он «уже видел». Это критично для корректного failover и безопасного переподключения реплик.
Готовы перейти на современную серверную инфраструктуру?
В King Servers мы предлагаем серверы как на AMD EPYC, так и на Intel Xeon, с гибкими конфигурациями под любые задачи — от виртуализации и веб-хостинга до S3-хранилищ и кластеров хранения данных.
- S3-совместимое хранилище для резервных копий
- Панель управления, API, масштабируемость
- Поддержку 24/7 и помощь в выборе конфигурации
Результат регистрации
...
Создайте аккаунт
Быстрая регистрация для доступа к инфраструктуре
Почему GTID обычно выигрывает в failover
- Проще выбрать кандидата на промоут: сравниваете
@@GLOBAL.gtid_executed/Executed_Gtid_Setи понимаете, кто впереди. - Переподключение реплик без координат:
CHANGE MASTER TO ... MASTER_AUTO_POSITION=1вместо подбора binlog‑координат. - Меньше ручных ошибок: меньше «человеческого фактора» на самых неприятных шагах. (Это не отменяет требований к дисциплине: fencing, read‑only, проверка на errant транзакции.)
Подготовка к аварийному переключению
Failover «в моменте» почти всегда выглядит красиво только у тех, кто подготовился заранее: настроил параметры, сделал наблюдаемость, решил вопрос «кто имеет право писать» и отрепетировал процедуру.
Ниже — минимальный практический набор.
Базовые требования GTID и репликационной цепочки
GTID должен быть включен на всех узлах: gtid_mode=ON и enforce_gtid_consistency=ON. MySQL явно указывает эти параметры как базовые для GTID‑репликации.
Для того чтобы реплика могла стать новым мастером (и чтобы другие реплики могли реплицироваться от неё), в топологии обычно требуется:
log_bin=ON(binlog включен) на репликах, которые потенциально станут мастером. MySQL в разделе про переключение источников отдельно отмечает, что реплики в такой группе обычно должны быть с включенным бинарным логированием.log_replica_updates=ON(или староеlog_slave_updates) — тогда реплика пишет в свой binlog изменения, примененные SQL‑потоком репликации; это нужно для chain‑репликации и для промоута.
Репликационные метаданные — в TABLE, а не в FILE (для crash‑safety):
- В MySQL 5.7 это задается
master_info_repository=TABLEиrelay_log_info_repository=TABLE. - В MySQL 8.0 таблицы‑репозитории — дефолт, а сами переменные считаются устаревающими (deprecated).
Смысл практический: метаданные применения транзакций коммитятся согласованно, поэтому прогресс репликации в таблицах остается консистентным даже при внезапной остановке.

Формат бинлога и типичные «грабли»
Для большинства продакшн‑сценариев безопаснее ROW‑репликация (или MIXED там, где нужно), потому что statement‑репликация может страдать от недетерминированных выражений и особенностей логирования (особенно со stored routines/trigger‑логикой). MySQL прямо говорит, что ряд проблем statement‑репликации можно избежать переходом на row‑based.
Дополнительно: изменение binlog_format считается deprecated (8.0.34) и в будущем ожидается, что останется только row‑формат.
Read-only дисциплина: чтобы «случайные записи» не убили GTID
Практика простая: все реплики должны быть read‑only, а write‑трафик должен идти только на одного writer’а.
super_read_only=ONполезен как «страховка от людей с привилегиями»: в этом режиме клиенты не могут писать даже с расширенными правами; при этом репликационные потоки продолжают применять изменения.read_onlyиsuper_read_only— это именно локальные флаги: они не реплицируются, что удобно для отсутствия сюрпризов при изменении роли узла.
Таймауты и наблюдаемость, чтобы не «проспать» фэйл
Для скорости детекта полезно понимать, как реплика решает «мастер пропал». MySQL описывает, что реконнекты запускаются, когда реплика достигает таймаута соединения (replica_net_timeout/slave_net_timeout) без данных/heartbeat.
Что мониторить «ежедневно», а не только в день аварии:
- статус потоков репликации (
Slave_IO_Running/Slave_SQL_Runningили их аналоги), лаги, ошибки. - GTID‑наборы:
@@GLOBAL.gtid_executed, а также вSHOW REPLICA STATUSполяRetrieved_Gtid_SetиExecuted_Gtid_Set(по ним часто видно реальную картину лучше, чем поSeconds_Behind_Master).
Отдельно о синтаксисе: начиная с MySQL 8.0.22 команды и статусы со словом SLAVE считаются deprecated (например, SHOW SLAVE STATUS, STOP SLAVE), вместо них используются SHOW REPLICA STATUS, STOP REPLICA и т. п.
Таблица ключевых переменных и рекомендуемых значений
| Переменная / опция | Рекомендация для GTID failover | Зачем это нужно | Комментарий по версии |
|---|---|---|---|
gtid_mode | ON | включает GTID‑транзакции | обязательно для GTID‑репликации |
enforce_gtid_consistency | ON | запрещает GTID‑небезопасные операции | нужно выставить до включения GTID |
MASTER_AUTO_POSITION | 1 в CHANGE MASTER TO | авто‑позиционирование по GTID | упрощает реконфигурацию/фэйл |
log_bin | ON | чтобы новый мастер имел binlog и историю | MySQL рекомендует binlog на репликах в failover‑группе |
log_replica_updates / log_slave_updates | ON | чтобы реплика логировала примененные изменения | требуется для топологий, где реплика может стать источником |
master_info_repository / relay_log_info_repository | TABLE | crash‑safe метаданные репликации | важно для 5.7; в 8.0 по умолчанию TABLE, переменные deprecated |
binlog_format | ROW | меньше недетерминизма и сюрпризов | statement‑проблемы решаются row‑форматом; ожидается future «row only» |
super_read_only | ON на репликах | защита от «случайных записей» | блокирует клиентские записи даже при привилегиях; репликационные потоки продолжают применять |
skip_replica_start / --skip-replica-start | включить на репликах | чтобы реплика не стартовала сама после рестарта | рекомендовано при GTID‑сетапе и в troubleshooting |
replica_net_timeout (slave_net_timeout) | подобрать под SLA (обычно ниже дефолта) | быстрее детектить пропажу источника | влияет на момент реконнекта/детекта |

Аварийное переключение: четыре сценария с командами и проверками
Перед командами — короткий принцип, который экономит часы жизни:
Failover = (fencing старого мастера) + (выбор лучшей реплики) + (промоут) + (переподключение остальных) + (проверка данных).
MySQL отдельно подчеркивает, что в failover‑ситуации переключение источника обычно «не ломает» структуру/целостность при условии, что все серверы исполняют одни и те же события, и вы аккуратно делаете переключение.
Диаграмма процесса failover
flowchart TD
A[Авария: master недоступен] --> B{Есть риск split-brain?}
B -->|Да| C[Остановить запись: fencing/VIP/DNS/Firewall/Proxy]
B -->|Нет| D[Переходим к выбору кандидата]
C --> D
D --> E[Собрать gtid_executed со всех реплик]
E --> F{Есть один "самый впереди" без errant GTID?}
F -->|Да| G[Промоут реплики в master]
F -->|Нет| H[Разбор расхождений: лаг/errant/split-brain]
G --> I[Переподключить реплики на новый master (AUTO_POSITION)]
I --> J[Проверки репликации + консистентности данных]
J --> K[Документировать инцидент, восстановить старый master как реплику]Таблица сценариев и рекомендуемых действий
| Сценарий | Цель | Рекомендуемый подход | Риск данных |
|---|---|---|---|
| Мастер недоступен, реплики синхронизированы | быстро вернуть запись | промоут самой «здоровой» реплики, остальные переподключить по MASTER_AUTO_POSITION=1 | минимальный для async‑репликации |
| Мастер недоступен, реплики расхождены | выбрать лучший источник правды | промоут реплики с максимальным gtid_executed, выявление errant GTID через GTID_SUBTRACT, отстающих догнать, «сомнительные» — пересобрать/проверить | средний/высокий |
| Split‑brain / двойной мастер | остановить хаос | немедленный fencing, затем выбор авторитетного мастера и, как правило, пересборка второй стороны + верификация данных | высокий |
| Возврат роли старому мастеру (failback) | вернуться к исходной архитектуре | поднять старый мастер как реплику нового (AUTO_POSITION), затем плановый switchover при «нулевом лаге»; при восстановлении из бэкапа корректно работать с gtid_purged | контролируемый (при плановом окне) |
Команды и ожидаемые выводы
| Задача | Команда | Ожидаемо в выводе |
|---|---|---|
| Проверить статус реплики | SHOW SLAVE STATUS\G (<=8.0.21) SHOW REPLICA STATUS\G (>=8.0.22)=8.0.21)> | Slave_IO_Running/Replica_IO_Running, Slave_SQL_Running/Replica_SQL_Running, Executed_Gtid_Set, Retrieved_Gtid_Set |
| Остановить репликацию | STOP SLAVE; / STOP REPLICA; | потоки receiver/applier остановятся; receiver перестанет писать в relay log, applier — применять |
| Остановить только IO или SQL | STOP REPLICA IO_THREAD; / STOP REPLICA SQL_THREAD; | удобно «досушить» relay log или заморозить применение |
| Переподключить к новому мастеру по GTID | CHANGE MASTER TO ... MASTER_AUTO_POSITION=1; | реплика отправит GTID‑сет и получит только недостающее |
| Сравнить GTID‑сеты | SELECT GTID_SUBTRACT(setA,setB); | увидите разницу (что есть в A и нет в B); аргументы — строки и должны быть в кавычках |
Сценарий: мастер недоступен, реплики синхронизированы
Ситуация: мастер не отвечает, но реплики были «в ноль» (или почти) и вы ожидаете, что у всех одинаковый Executed_Gtid_Set. Тут GTID дает максимально «скучный» промоут.
Шаги
Шаг первый — исключить split‑brain (fencing).
Если мастер «недоступен для вас», но доступен часть сети/приложений — это классическая ловушка. Практический fencing: отключить writer‑VIP, выключить маршрут, закрыть порт 3306 правилами firewall, остановить mysqld, отключить пул коннектов на приложениях (что у вас принято). Цель — гарантировать, что в старый мастер никто не пишет.
Шаг второй — выбрать кандидата.
На каждой реплике:
SHOW REPLICA STATUS\G
SELECT @@GLOBAL.gtid_executed\G
Ищем, чтобы Executed_Gtid_Set совпадал и не было ошибок. SHOW SLAVE STATUS в MySQL 8.0.22+ deprecated, но обычно работает как алиас.
Пример ожидаемого фрагмента SHOW REPLICA STATUS\G:
Replica_IO_Running: No
Replica_SQL_Running: Yes
Seconds_Behind_Source: 0
Retrieved_Gtid_Set: 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-84210
Executed_Gtid_Set: 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-84210
Auto_Position: 1
Шаг третий — «досушить» relay log на кандидате (если нужно).
Если IO‑поток уже мертв из‑за недоступного мастера, но SQL‑поток еще применяет relay log, дайте ему закончить:
STOP REPLICA IO_THREAD;
-- ждём, пока SQL догонит
SHOW REPLICA STATUS\G
Остановка/старт потоков в MySQL описана явно: receiver читает и пишет в relay log, applier читает relay log и применяет.
Шаг четвертый — остановить репликацию и промоутить кандидата в мастер.
На кандидате (реплике, которую делаем мастером):
STOP REPLICA; -- или STOP SLAVE;
RESET REPLICA ALL; -- или RESET SLAVE ALL; (удалит параметры подключения)
SET GLOBAL super_read_only = OFF;
SET GLOBAL read_only = OFF;
SHOW MASTER STATUS\G
RESET REPLICA — актуальное имя команды в MySQL 8.0.22+ (вместо RESET SLAVE).
Важно: RESET MASTER на новом мастере в рамках failover обычно не нужен и опасен — он сбрасывает GTID‑историю выполнения и чистит mysql.gtid_executed.
Шаг пятый — переподключить остальные реплики к новому мастеру.
На каждой оставшейся реплике:
STOP REPLICA;
CHANGE MASTER TO
MASTER_HOST='new-master.example',
MASTER_USER='repl',
MASTER_PASSWORD='***',
MASTER_PORT=3306,
MASTER_AUTO_POSITION=1;
START REPLICA;
SHOW REPLICA STATUS\G
Смысл MASTER_AUTO_POSITION=1: реплика в handshake передает набор уже выполненных транзакций, а мастер отдает недостающие.

Сценарий: мастер недоступен, реплики расхождены
Ситуация: кто‑то отстал, кто‑то догнал, а иногда у одной реплики есть «лишние» GTID (errant transactions). В этом сценарии важно не столько «быстро», сколько «правильно».
Развилка: отставание vs errant GTID
- Обычный лаг (replica просто отстала) — лечится выбором самой актуальной реплики и переподключением остальных (они догонят по GTID).
- Errant транзакции — транзакции, выполненные напрямую на реплике (в обход мастера). Percona описывает это именно так: errant transactions — это транзакции, которые существуют только на конкретной реплике, потому что были выполнены на ней.
Практический алгоритм выбора кандидата
На каждой реплике снимите:
SELECT @@GLOBAL.gtid_executed AS gtid\G
Далее сравните наборы через GTID_SUBTRACT() — MySQL подчеркивает, что функции GTID работают со строковыми представлениями GTID‑сетов, поэтому аргументы должны быть в кавычках.
Пример: сравнить реплику R2 с кандидатом R1:
-- Что есть у R2, чего нет у R1:
SELECT GTID_SUBTRACT('R2_GTID_SET', 'R1_GTID_SET')\G
- Если результат пустой (или
NULL/пустая строка) — R2 не «впереди» R1. - Если не пустой — R2 содержит транзакции, отсутствующие у кандидата: это либо потерянные транзакции мастера, либо errant, либо особенности multi‑source/фильтров.
Дополнение по фильтрам: в GTID‑репликации GTID «не фильтруются» так же, как данные; это сделано, чтобы GTID‑набор реплики оставался согласованным с источником и auto‑positioning не ломался.
Если есть реплики «впереди» кандидата
Самый безопасный общий путь:
- Промоутить реплику с максимальным и «чистым»
gtid_executed(которая является супермножеством для остальных). - Реплики с «лишними» GTID (errant) — не делать их читателями «как ни в чем не бывало», пока не проверите данные.
Иногда пытаются «выровнять GTID» пустыми транзакциями (SET GTID_NEXT=...; BEGIN; COMMIT;). MySQL описывает эту технику (injecting empty transactions), но там же предупреждает о риске «залить» поток ложными транзакциями и необходимости чистки binlog’ов после таких манипуляций.
Практический вывод: это инструмент для восстановления/провижининга и отдельных кейсов, а не универсальная «таблетка» от расхождений.

Сценарий: split-brain или двойной мастер
Ситуация: два узла принимали запись параллельно (часто из‑за сетевой сегментации или ошибочной логики VIP/Proxy). Это не «репликация сломалась», это «у вас две истории мира».
Честная реальность: автоматом красиво «склеить» два мастера нельзя без знания предметной области данных (конфликты уникальности, разные обновления одних строк и т. п.). Поэтому процедура в большинстве команд выглядит так:
- Немедленно остановить запись в оба узла, кроме одного выбранного «источника правды».
- Выбрать авторитетный мастер (обычно тот, к которому шел основной write‑трафик).
- Вторую сторону пересобрать (полная пересинхронизация) или выполнять очень аккуратную ручную консолидацию транзакций (если вы точно понимаете, что делаете).
- После стабилизации — проверка консистентности.
Инструменты типа Orchestrator полезны тем, что визуализируют топологию и умеют безопасно «перетаскивать» реплики между мастерами, отклоняя незаконные перестройки (например, там, где нарушаются правила репликации).

Сценарий: восстановление старого мастера и возврат роли
Важный принцип: вернувшийся старый мастер нельзя просто включить обратно «как было». Его задача — сначала стать репликой нового мастера и догнать состояние.
Вариант: старый мастер вернулся с теми же данными (не переустанавливали)
- Сразу заблокируйте записи на старом мастере:
SET GLOBAL super_read_only = ON;
super_read_only запрещает клиентские обновления даже с привилегиями; при этом репликационные потоки продолжают применять.
- Подключите его как реплику к текущему мастеру (теперь это «источник»):
STOP REPLICA;
CHANGE MASTER TO
MASTER_HOST='current-master.example',
MASTER_USER='repl',
MASTER_PASSWORD='***',
MASTER_AUTO_POSITION=1;
START REPLICA;
SHOW REPLICA STATUS\G
- Дождитесь нулевого лага и совпадения GTID‑наборов.
Вариант: старый мастер восстановлен из бэкапа/снапшота (GTID нужно выставить корректно)
Если вы восстанавливаете узел из снапшота и хотите «вклеить» его в GTID‑цепочку, MySQL описывает подход: можно выставить gtid_purged на реплике по значению gtid_executed с сервера, откуда брали бэкап — без необходимости «инжектить пустые транзакции».
При этом помните ограничения:
RESET MASTERна GTID‑узле сбрасывает GTID‑историю выполнения (обнуляетgtid_purged,gtid_executedи чиститmysql.gtid_executed).- В MySQL 5.6/5.7 изменить
gtid_purgedможно только когдаgtid_executedпуст (для ряда операций). - В MySQL 8.0 есть больше гибкости (в том числе можно выбирать режим «заменить» или «добавить» набор в
gtid_purged).
Типовой шаблон (осторожно, выполняйте только если понимаете последствия):
-- на восстановленном узле, до подключения к репликации
RESET MASTER; -- только если нужно обнулить GTID-историю на "чистом" экземпляре
SET GLOBAL gtid_purged = 'UUID_OF_CLUSTER:1-84210';
CHANGE MASTER TO
MASTER_HOST='current-master.example',
MASTER_USER='repl',
MASTER_PASSWORD='***',
MASTER_AUTO_POSITION=1;
START REPLICA;
Проверка целостности данных и работа с relay log и «незавершенными» транзакциями
Что происходит с транзакциями «в полете»
- Незакоммиченные транзакции мастера не попадают в binlog, а значит и не могут быть реплицированы; при аварии они либо откатываются, либо «не существовали» для репликации.
- Relay log на реплике — это локальный буфер событий, полученных от мастера: receiver записывает, applier применяет. Остановка репликации останавливает оба: receiver перестает читать binlog источника и писать relay log, applier перестает читать relay log и выполнять.
- При многопоточной репликации (
replica_parallel_workers > 0) MySQL закрывает «дырки» в последовательности при штатномSTOP REPLICA, но предупреждает: если реплика остановлена неожиданно во времяSTOP REPLICA, последовательность примененных транзакций из relay log может стать неконсистентной.
Практический вывод: в момент промоута лучше останавливать репликацию штатно и, если возможно, дать SQL‑потоку «досушить» relay log.
Проверка консистентности: pt-table-checksum и pt-table-sync
Для регулярной (и пост‑инцидентной) проверки очень полезен Percona Toolkit:
pt-table-checksumделает онлайн‑проверку консистентности репликации, выполняя checksum‑запросы на источнике; реплики, где данные отличаются, будут иметь другие результаты.pt-table-syncможет синхронизировать различия, но он очень требователен к безопасной модели записи: инструмент прямо сообщает, что это небезопасно писать напрямую в реплики, и завершится с ошибкой, если обнаружит, что destination — реплика.
Для failover‑практики это удобно так:
- после промоута —
pt-table-checksum(по возможности на «новом мастере» как источнике правды); - если есть расхождения — принять решение: чинить точечно
pt-table-sync(очень аккуратно, чаще через источник), либо пересобрать проблемную реплику заново.

Best practices: как сделать failover «скучным»
Минимальный набор «чтобы не горело»
- Один writer, все остальные —
super_read_only=ON, плюс процессный контроль (кто и как имеет право снять read‑only). - Всегда включайте авто‑позиционирование (
MASTER_AUTO_POSITION=1) и проверяйте, что оно реально включено на всех репликах (бывают «ghosted GTID»‑ситуации, когда GTID есть, но auto‑position выключен — и тогда failover снова превращается в ручную боль). - Crash‑safe репликация: репозитории метаданных в TABLE (особенно если вы еще на 5.7).
- Стабильный формат логирования (обычно
ROW) и единая политика поbinlog_format(избегайте неожиданных переключений формата на лету, особенно при наличии временных таблиц). - Тестируйте failover как учение, а не как сюрприз: симулируйте «мастер пропал», отрабатывайте fencing, промоут, переподключение, проверку данных.

Автоматизация: mysqlrpladmin/mysqlfailover, Orchestrator, MHA
Если вы хотите быстрее, чем «вручную по SSH»:
- MySQL Utilities (
mysqlrpladmin,mysqlfailover) исторически поддерживали промоут/фэйл при GTID: Percona в серии статей прямо пишет, что при GTID‑репликации эти утилиты можно использовать для promotion (manual и auto).
Но есть нюанс по жизненному циклу: Oracle отмечает, что MySQL Utilities покрыты Sustaining Support, а часть функций планировалась/планируется в MySQL Shell; репозиторийmysql/mysql-utilitiesна GitHub архивирован и read‑only. - Orchestrator — сервис/инструмент управления репликационными топологиями: умеет discovery, визуализацию, понимает GTID и отбраковывает «нелегальные» перестройки. (Плюс у Percona есть отдельный материал про работу с errant GTID через orchestrator.)
- MHA (Master High Availability Manager) — отдельный классический инструмент для автоматизации master failover и быстрого switchover; репозиторий менеджера описывает его именно как средство автоматизации failover и master switch.
Практический совет для «среднего» уровня зрелости: начните с четкой ручной runbook‑процедуры + скриптов, а уже потом накрывайте это orchestrator/HA‑менеджерами.
Шаблон bash-скрипта для промоута и переподключения реплик
Ниже — скелет, который удобно адаптировать под вашу схему (VIP/ProxySQL/DNS). Он специально использует .my.cnf (или MYSQL_PWD) вместо пароля в командной строке.
#!/usr/bin/env bash
set -euo pipefail
# Usage:
# ./gtid_failover.sh new_master_host replica1 replica2 replica3
#
# Preconditions:
# - fencing already done (old master cannot accept writes)
# - MySQL user has privileges to STOP/START REPLICA and CHANGE MASTER
# (SYSTEM_VARIABLES_ADMIN/SUPER may be required to set globals) citeturn5search1
NEW_MASTER="${1:?new master host required}"
shift
REPLICAS=("$@")
MYSQL_OPTS=(--protocol=tcp --connect-timeout=3 --batch --skip-column-names)
mysqlq () {
local host="$1"; shift
mysql "${MYSQL_OPTS[@]}" -h "$host" "$@"
}
echo "[1/5] Promoting ${NEW_MASTER}..."
# Stop replication & make it writable
mysqlq "$NEW_MASTER" -e "STOP REPLICA; RESET REPLICA ALL;"
mysqlq "$NEW_MASTER" -e "SET GLOBAL super_read_only=OFF; SET GLOBAL read_only=OFF;"
mysqlq "$NEW_MASTER" -e "SHOW MASTER STATUS\G" || true
echo "[2/5] Repointing replicas to ${NEW_MASTER} using GTID auto-position..."
for r in "${REPLICAS[@]}"; do
echo " -> $r"
mysqlq "$r" -e "STOP REPLICA;"
mysqlq "$r" -e "
CHANGE MASTER TO
MASTER_HOST='${NEW_MASTER}',
MASTER_PORT=3306,
MASTER_USER='repl',
MASTER_PASSWORD='***',
MASTER_AUTO_POSITION=1;
"
mysqlq "$r" -e "START REPLICA;"
done
echo "[3/5] Checking replica status..."
for r in "${REPLICAS[@]}"; do
echo "===== $r ====="
mysqlq "$r" -e "SHOW REPLICA STATUS\G" | egrep -i 'Replica_(IO|SQL)_Running|Seconds_Behind|Executed_Gtid_Set|Retrieved_Gtid_Set|Last_(IO|SQL)_Error|Auto_Position' || true
done
echo "[4/5] Done. Now switch application writer endpoint (VIP/DNS/ProxySQL)."
echo "[5/5] Run pt-table-checksum for post-failover consistency check." # citeturn0search3
Предупреждение: этот шаблон не решает split‑brain и не разбирает errant GTID — эти условия должны быть отсечены до запуска, иначе скрипт может сделать ситуацию хуже быстрее, чем вы успеете моргнуть.

Главные риски и как их снижать
Риск «номер один» — двойной мастер. Он почти всегда возникает не из‑за MySQL, а из‑за отсутствия fencing и контроля writer‑эндпойнта. Решение: жесткая процедура отключения старого мастера от записи + дисциплина super_read_only для всех остальных.
Риск «номер два» — расхождение данных (silent drift) из‑за errant транзакций. Решение: мониторить GTID‑сеты, запретить записи на реплики, регулярно гонять pt-table-checksum.
Риск «номер три» — ошибки при остановке/промоуте (особенно при параллельном applier): если реплика «убита» в момент STOP REPLICA, могут появиться несогласованности применения relay log. Решение: штатная остановка, контроль SQL‑потока, и crash‑safe метаданные в TABLE.
Если GTID‑failover кажется «слишком простым», это хороший знак: он и должен быть простым — при условии, что вы заранее сделали его безопасным.