Відновлення після відмови — це момент, коли добрі дизайни реплікації проходять випробування. На діаграмах усе виглядає акуратно: primary, replica, VIP, можливо шар проксі, і runbook, який ніхто не читає до третьої ранку. Потім зависає диск, трапляється мережевий збій, і раптом «новий primary» відмовляється приймати записи, репліки вказують не на ту хост-машину або ще гірше: два primary приймають трафік і ваш бізнес винаходить новий вид бухгалтерії.
MySQL і MariaDB обидва «підтримують реплікацію». Вони не переключаються однаково в реальному житті. Різниці рідко пов’язані з явними функціями заголовків і майже завжди — з нестандартною поведінкою на краях: семантика GTID, сумісність binlog, метадані, припущення проксі і ті операційні дрібні проблеми, що проявляються тільки під навантаженням.
Фактично: реплікація — це не відновлення після відмови
Реплікація переміщує зміни даних з A до B. Відновлення після відмови переміщує повноваження з A до B, плюс трафік, плюс засоби безпеки. Саме в цих засобах заховані проблеми.
У продакшені відновлення після відмови має три завдання:
- Вибрати правильного кандидата (найближчий до актуальних даних, у найкращому стані, не в середині відновлення після збою).
- Зробити його записуваним (і гарантувати, що старий primary не зможе приймати записи, навіть якщо він повернеться «злим»).
- Переадресувати клієнтів (проксі, DNS, VIP, connection pool-и, кеші додатків і «допоміжна» логіка повторних спроб).
MySQL і MariaDB обидва підтримують асинхронну реплікацію. Обидва можуть використовувати GTID (з важливими відмінностями). Обидва можуть мати напівсинхронні варіанти. Обидва можуть керуватися оркестраторами або проксі. І обидва можуть вас підвести, якщо ви сприймаєте реплікацію як чарівну паличку, а не контракт.
Операційна істина, яку варто наклеїти на стікер: відновлення після відмови — це рішення про узгодженість. Ви завжди обираєте між доступністю зараз і коректністю даних пізніше. Ваші інструменти повинні робити це рішення явним, а не прихованим.
Жарт №1: Реплікаційне відставання — це просто база даних, що практикує усвідомленість: живе в минулому, щоб не хвилюватися про майбутнє.
Перш ніж порівнювати MySQL і MariaDB, давайте визначимо словник, яким SRE користуються на практиці:
- Підвищення (Promotion): перетворення репліки на записуваний primary.
- Перенаправлення (Repointing): змінення реплік, щоб вони слідували за новим primary.
- Фенсинг (Fencing): заборона старому primary приймати записи (зупинка сервісу, ізоляція в мережі, STONITH, фензинг сховища).
- Split brain: два вузли приймають записи для одного й того ж набору даних. Це не «інцидент», це «нова ера ваших даних».
- Інструмент відновлення після відмови: Orchestrator, MHA, MaxScale, скрипти ProxySQL, Pacemaker/Corosync, оператори Kubernetes або ваш власний bash, який ви вважаєте «тимчасовим».
Цікаві факти та історія, що важать і нині
Це не факти для вікторини. Вони пояснюють, чому відновлення реплікації MySQL і MariaDB відрізняється в тонких операційних випадках.
- MariaDB відгалузилась від MySQL у 2009 році після шляху придбань MySQL (Sun, потім Oracle). Розбіжності почалися повільно, потім прискорилися, особливо навколо GTID і функцій реплікації.
- MySQL 5.6 представив GTID як першу масову реалізацію «global transaction ID» в Oracle MySQL; MariaDB реалізувала інший формат і поведінку GTID.
- Багатопотокова реплікація MySQL дозрівала пізніше (особливо 5.7+ з покращеннями паралельної реплікації, потім 8.0). MariaDB пішла власними налаштуваннями; рецепти тюнінгу не переносяться без змін.
- MySQL 5.7 зробив InnoDB за замовчуванням і підштовхнув поліпшення надійності навколо відновлення після збою та метаданих реплікації. MariaDB також вдосконалювала InnoDB (та варіанти як XtraDB історично), але операційні симптоми різняться.
- MySQL 8.0 змінив значення за замовчуванням і прибрав стару поведінку (наприклад, кеш запитів давно пішов, але важливіше — плагіни автентифікації та поведінка метаданих). Перемішування версій може падати з причин, що здаються не пов’язаними з реплікацією.
- MariaDB додала «domain IDs» у GTID, що корисно (multi-source) і заплутано (неправильне керування доменами під час підвищення).
- Group Replication/InnoDB Cluster у MySQL — це офіційний шлях до високої доступності в Oracle MySQL; MariaDB має Galera-кластер і свою HA-стратегію. Люди порівнюють асинхронну реплікацію з failover, хоча насправді хочуть консенсусну систему.
- Екосистеми інструментів розвивалися окремо: Orchestrator почався в MySQL-середовищі, але може працювати з MariaDB; MaxScale — проксі MariaDB з поведінкою, оптимізованою під MariaDB. Змішування інструментів і припущення паритету функцій — часта причина помилок.
Що ламається під час відновлення після відмови (MySQL vs MariaDB)
1) Failover з GTID: «GTID — отже безпечно»… поки ні
Failover на основі GTID має спростити підвищення: вибрати найактуальнішу репліку, підвищити її і перенаправити інші, використовуючи автопозиціонування GTID. На практиці GTID не означає «без втрати даних», це означає «краще ведення обліку».
Поведінка GTID у MySQL зазвичай зосереджена навколо gtid_executed, gtid_purged і множин GTID-наборів. Інструменти відновлення часто використовують порівняння множин GTID MySQL, щоб знайти найпросунутішу репліку.
Поведінка GTID у MariaDB використовує інший формат GTID і включає поняття domain ID та порядкових номерів. Це потужна модель, але вона робить «просто порівняти GTID» менш уніфікованим у змішаних кластерах.
Що ламається:
- Несумісність строгості GTID: репліка може не прийняти певні GTID після підвищення через розриви, очищення історії або відмінності конфігурації.
- Інструменти, що припускають MySQL-стиль змінних GTID, падають на MariaDB (або інтерпретують їх неправильно).
- Failover між різними версіями: «найкраща» репліка на трохи іншій версії з іншими значеннями за замовчуванням; підвищення проходить, але клієнти падають.
2) Метадані реплікації стають дивними під навантаженням
Під час відновлення ви робите багато швидких переходів станів: зупиняєте реплікацію, скидаєте реплікаційну інформацію, змінюєте primary, запускаєте реплікацію і іноді перебудовуєте relay log-и. Деталі відрізняються між MySQL і MariaDB, включаючи синтаксис команд і поля статусу.
У MySQL 8.0 команди реплікації змінилися (START REPLICA замість START SLAVE), назви статусів змінилися (SHOW REPLICA STATUS), а метадані перемістились в більш транзакційні таблиці. MariaDB довше трималася за традиційну номенклатуру й має власну поведінку щодо relay log-ів і GTID.
Що ламається:
- Runbook-и, написані під один синтаксис, виконуються проти іншого о 3 ранку і тихо нічого не роблять.
- Таблиці з інформацією про реплікацію стають неконсистентними після часткових команд (особливо коли автоматизація таймаутиться під час зміни).
- Пошкодження relay log-ів після збою, і «просто» підвищення перетворюється на перебудову репліки.
3) Semi-sync: затримки і налаштування безпеки не відповідають ментальній моделі
Напівсинхронна реплікація зменшує «прогалину підтверджень», вимагаючи як мінімум однієї репліки, щоб підтвердити отримання перед поверненням commit. Це звучить як гарантія надійності. Але це не те саме, що гарантія відсутності втрат при відмові, якщо ви не розумієте семантику підтверджень і таймаутів.
Плагін semi-sync у MySQL і реалізація в MariaDB схожі по духу, але операційно ви побачите різні лічильники статусу і різні сигнатури відмов. Ризик однаковий: коли semi-sync переключається на async під час турбулентності, ваше RPO може змінитися саме тоді, коли це найменш бажано.
Що ламається:
- Failover припускає «немає втрати даних», бо semi-sync було ввімкнено, але він вже деградував до async через таймаути.
- Надто агресивні таймаути спричиняють часті відключення, що збільшує джиттер затримки commit-ів і викликає повторні спроби в додатку, які підсилюють навантаження.
4) Проксі та VIP: база може бути OK, але трафік — ні
Більшість невдач при відновленні трапляються на шарі клієнта. Проксі кешують стан бекендів, додатки кешують DNS, connection pool-и прив’язують сесії, а деякі драйвери поводяться як діти, коли їх улюблений ендпоінт зникає.
Різниці між MySQL і MariaDB проявляються, коли ви використовуєте інструменти екосистеми:
- MaxScale (проксі MariaDB) може робити моніторно-кероване відновлення з припущеннями, специфічними для MariaDB. Воно може працювати з MySQL, але уважно перевіряйте межі підтримки.
- Orchestrator (поширений у MySQL-флітах) чудовий, але має власні погляди. Підтримка MariaDB є, але інтерпретація GTID може різнитися залежно від версій.
- ProxySQL більш нейтральний щодо бази, але покладається на health checks і правила запитів; ви повинні правильно визначити «writer» і «reader» і тримати його в курсі логіки підвищення.
Що ламається: ви правильно підвищили вузол, але проксі все ще шле записи на старий primary протягом хвилини. Вітаємо, ви щойно створили split brain з додатковими кроками.
5) Недетермінованість і примари statement-based реплікації
Якщо ви досі використовуєте statement-based реплікацію (SBR), ви обираєте біль. Row-based реплікація (RBR) — сучасний дефолт з причин: вона уникає недетермінованої поведінки, яка перетворює відновлення після відмови на експертизу криміналіста.
Деякі розгортання MariaDB тримають змішані формати з причин сумісності. Деякі інсталяції MySQL теж, зазвичай після довгого міграційного періоду, де «тимчасове» стало постійним.
Що ламається:
- Репліка застосовує оператор інакше через різницю часових зон, колацій, SQL mode або поведінки функцій між версіями.
- Підвищення виявляє прихований дрейф: у промотованому вузлі дані тонко відрізняються, не очевидно пошкоджені.
6) «read_only» — це не фензинг
І MySQL, і MariaDB підтримують read_only і super_read_only (наявність залежить від версії). Але це порадні контролі, а не жорсткий фензинг. Користувачі з достатніми привілеями все ще можуть писати в багатьох сценаріях (особливо без super_read_only), і фонові завдання також можуть робити записи.
Плани відновлення, що покладаються на встановлення read_only=ON на старому primary, припускають, що старий primary буде співпрацювати. Старі primary не співпрацюють. Вони нудьгують, а потім приймають записи, щойно з’явиться хитре підключення.
7) Резервне копіювання/відновлення під час перевідтворення: GTID і налаштування binlog важать
Після відновлення часто потрібно перебудувати репліку. Якщо ваш інструмент бекапу не зберігає стан GTID правильно, або якщо ви відновлюєте з неправильними налаштуваннями binlog, ви отримаєте репліку, яка не зможе приєднатися коректно.
Розбіжності MySQL vs MariaDB проявляються у поводженні зі станом GTID, а також у відмінностях конфігурації за замовчуванням і змінних реплікації. Бекапи, що «сумісні з MySQL», не завжди «сумісні з MariaDB для позиціювання GTID під час відновлення», особливо при перетині версій.
Реальність GTID: схожий акронім, інша угода
Говорять «ми використовуємо GTID», наче це питання так/ні. Ні. Це «ми використовуємо GTID, з такими обмеженнями, і ми знаємо, що трапиться, коли ці обмеження порушаться».
MySQL GTID: множини та автопозиціонування
MySQL GTID зазвичай керується через:
gtid_mode=ONenforce_gtid_consistency=ONlog_slave_updates=ON(щоб репліки могли стати primary)MASTER_AUTO_POSITION=1(автопозиціонування)
Інструменти відновлення люблять MySQL GTID, бо можна порівнювати набори GTID. Але є підводний камінь: набори GTID кажуть, що виконано, а не що ваші клієнти вважають підтвердженим, якщо старий primary помер посеред операції. Semi-sync допомагає, але може деградувати.
MariaDB GTID: domain ID і операційні гострі кути
MariaDB GTID інша. Domain ID можуть представляти різні реплікаційні домени. Це корисно для multi-source реплікації та відстеження потоків. Але це також вводить режими відмови, коли підвищений вузол має GTID-стан, що виглядає «повним», але не відповідає очікуванням інших реплік.
MariaDB також має налаштування типу строгого режиму GTID (залежно від версії), які змінюють, чи дозволяються розриви. Це добре для коректності, але може спричинити жорстке відхилення підвищення саме тоді, коли хочеться м’якої посадки.
Що робити з цим знанням
Якщо у вас однорідне середовище (тільки MySQL або тільки MariaDB), обирайте нативний шлях GTID і дотримуйтеся його. Не ставте GTID як галочку для міграції. Розглядайте його як проєкт: метод бекапу, перебудова репліки, процедура підвищення та моніторинг мають співпадати.
Якщо у вас змішане середовище (MySQL і MariaDB), припиніть вдавати, що відновлення симетричне. Або розділіть інструменти підвищення для кожного двигуна, або стандартизуйте один двигун для ролі писача. Змішана реплікація може працювати для деяких випадків, але відновлення після відмови — перше місце, де виявляються невідповідності.
Вибір топології, що змінює зону впливу
Асинхронна primary-replica з ручним відновленням: чесна базова модель
Це найпростіша система, яка може працювати. Вона також змушує людей розуміти компроміси, бо людина натискає кнопку.
Плюси: простіше розуміти; менше складових. Мінуси: повільніше відновлення; людські помилки; процедура неоднорідна, якщо її не відточувати.
Асинхронна з оркестрованим відновленням: автоматизація, яка може нашкодити швидше
Оркестроване відновлення відмінне, коли воно спроєктоване з фенсингом і уважним вибором кандидата. Небезпечне, коли це «монітор бачить primary вниз → підвищує щось». Монітор — не ваш шар для гарантії коректності даних.
Вибір кандидата має враховувати:
- Відставання реплікації і час застосування (не тільки те, що IO-потік запущено)
- Випадкові транзакції (особливо з GTID)
- Стан сервера (диск заповнений, відновлення після збою, помилки файлової системи)
- Мережеву досяжність від клієнтів (промотований вузол у відмерлій підмережі — швидкий простій)
Мрії про «multi-primary»: Group Replication, Galera і реальність
Якщо вам насправді потрібне автоматичне відновлення без втрат даних, ви, ймовірно, дивитесь на систему на основі консенсусу. У MySQL це зазвичай Group Replication/InnoDB Cluster. У MariaDB — Galera-рішення.
Але: це не «реплікація з відновленням», це інші звірі з власними режимами відмови, характеристиками продуктивності і операційними вимогами.
Асинхронна реплікація з відновленням може бути цілком прийнятною, якщо ви погоджуєтеся на RPO>0 і маєте добрий фензинг. Це також може стати помилкою, якщо ви вдаєте, що вона гарантує сильну узгодженість.
План швидкої діагностики
Коли відновлення йде не так, вам потрібно швидко знайти вузьке місце. Не філософське; реальне — те, що заважає безпечно відновити записувальний сервіс.
Перше: встановіть авторитет і запобіжіть split brain
- Хто зараз записує? Перевірте
read_only/super_read_onlyі чи клієнти успішно пишуть. - Відфенсьте старий primary (зупинка, ізоляція мережі, вимкнення VIP, блокування на проксі). Якщо ви не можете відфенсити — у вас не відновлення, а ставка.
- Зупиніть автоматику, яка може повторно підвищувати, перенаправляти або перемикати VIP в обидва боки.
Друге: знайдіть найкоректнішого кандидата
- Порівняйте позиції GTID (або binlog file/pos, якщо немає GTID). Визначте репліку(и), найближчі до старого primary.
- Перевірте стан SQL-потоку: застосовується? припинено? помилка? чекає метаданичного локу?
- Перевірте здоров’я InnoDB: чи триває відновлення після збою, чи є беклог скидання брудних сторінок, чи переповнення IO.
Третє: перемістіть трафік, не брешучи додатку
- Оновіть проксі/VIP/DNS і перевірте з кількох підмереж додатків.
- Підтвердіть розділення writer/reader у проксі; переконайтеся, що записи справді потрапляють на новий primary.
- Слідкуйте за рівнем помилок і затримками. «Успішне» відновлення, що спричиняє 10х збільшення затримки — це все ще аутейдж, просто тихіший.
Четверте: відновіть набір реплік
- Переналаштуйте репліки на новий primary і перевірте, що вони коректно реплікуються.
- Перебудуйте репліки з пошкодженими relay log-ами або з випадами транзакцій.
- Підтвердіть, що бекапи і збереження binlog достатні для майбутніх перебудов.
Практичні завдання: команди, виводи та рішення
Це завдання, які ви фактично виконуєте під час брудного відновлення. Кожне містить: команду, що типовий вивід означає і яке рішення з цього випливає. Команди наведені так, ніби у вас є shell-доступ і клієнт MySQL.
Завдання 1: швидко визначити двигун/версію (синтаксис і змінні відрізняються)
cr0x@server:~$ mysql -N -e "SELECT VERSION(), @@version_comment;"
10.6.18-MariaDB-log MariaDB Server
Що це означає: Ви на MariaDB 10.6. Не вставляйте команди реплікації MySQL 8.0 без перевірки.
Рішення: Використовуйте поля статусу MariaDB (SHOW SLAVE STATUS) і змінні GTID MariaDB. Підготуйте відповідну гілку runbook-а.
Завдання 2: перевірити, чи сервер зараз записуваний
cr0x@server:~$ mysql -N -e "SELECT @@read_only, @@super_read_only;"
0 0
Що це означає: Цей вузол може приймати записи.
Рішення: Якщо це не передбачений primary під час інциденту, ви маєте відфенсити або негайно перемкнути його в read-only (а також виправити маршрутизацію трафіку).
Завдання 3: підтвердити потоки реплікації і відставання (приклад імен MySQL 8)
cr0x@server:~$ mysql -e "SHOW REPLICA STATUS\G" | egrep "Replica_IO_Running|Replica_SQL_Running|Seconds_Behind_Source|Last_SQL_Error"
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 2
Last_SQL_Error:
Що це означає: Реплікація працює з мінімальним відставанням.
Рішення: Ця репліка з погляду відставання — добрий кандидат для підвищення. Все ж перевірте стан GTID і здоров’я.
Завдання 4: та ж перевірка для MariaDB (назви полів інші)
cr0x@server:~$ mysql -e "SHOW SLAVE STATUS\G" | egrep "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|Last_SQL_Error"
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
Last_SQL_Error:
Що це означає: MariaDB-репліка наздогнала (наскільки цей показник може сказати).
Рішення: Все одно перевірте, що вона не брешуть: порівняйте позиції GTID/binlog і журнали помилок, якщо primary помер некоректно.
Завдання 5: перевірити стан виконання GTID (MySQL)
cr0x@server:~$ mysql -N -e "SELECT @@gtid_executed\G"
*************************** 1. row ***************************
@@gtid_executed: 3E11FA47-71CA-11E1-9E33-C80AA9429562:1-984321
Що це означає: Цей вузол виконав GTID до 984321 для того UUID-сервера.
Рішення: Порівняйте між репліками; та, що має супермножину, зазвичай кращий кандидат (за винятком випадків з errant transactions і здоров’ям).
Завдання 6: перевірити стан GTID (MariaDB)
cr0x@server:~$ mysql -N -e "SELECT @@gtid_current_pos, @@gtid_slave_pos;"
0-1-542118 0-1-542118
Що це означає: Домен 0, server_id 1, послідовність 542118; репліка застосувала до цієї позиції.
Рішення: Порівняйте кандидатів. Якщо бачите несподівані кілька доменів — зупиніться і відобразіть, що вони означають перед підвищенням.
Завдання 7: виявити розрив реплікації через відсутній рядок (поширено після небезпечних налаштувань)
cr0x@server:~$ mysql -e "SHOW SLAVE STATUS\G" | egrep "Last_SQL_Errno|Last_SQL_Error"
Last_SQL_Errno: 1032
Last_SQL_Error: Could not execute Delete_rows event on table app.orders; Can't find record in 'orders', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND
Що це означає: Є дрейф даних; репліка не може застосувати події коректно.
Рішення: Не підвищуйте цю репліку, якщо не приймаєте корупцію. Перебудуйте з чистої резервної копії або виправте дрейф цілеспрямовано (pt-table-checksum підхід—як дозволяє ваше середовище).
Завдання 8: підтвердити формат binlog і налаштування надійності (готовність до підвищення)
cr0x@server:~$ mysql -N -e "SELECT @@log_bin, @@binlog_format, @@sync_binlog, @@innodb_flush_log_at_trx_commit;"
1 ROW 1 1
Що це означає: Бінарне логування ввімкнено, формат рядковий, і налаштування більш-менш надійні (sync_binlog=1, flush при commit).
Рішення: Добре. Якщо бачите sync_binlog=0 або innodb_flush_log_at_trx_commit=2, припускайте вищий ризик втрати підтверджених транзакцій при краші — це змінює ваші очікування RPO при відновленні.
Завдання 9: перевірити, чи semi-sync справді активний (лічильники плагіна MySQL)
cr0x@server:~$ mysql -e "SHOW STATUS LIKE 'Rpl_semi_sync_master_status';"
+-----------------------------+-------+
| Variable_name | Value |
+-----------------------------+-------+
| Rpl_semi_sync_master_status | OFF |
+-----------------------------+-------+
Що це означає: Semi-sync наразі не активний, незалежно від конфігурації. Ймовірно він впав у async.
Рішення: Під час відновлення припускайте, що деякі транзакції можуть бути відсутні на репліках. Вибирайте кандидата за фактичним застосованим станом, а не за очікуваною безпекою.
Завдання 10: перевірити, що «старий primary» дійсно відфенсений
cr0x@server:~$ ssh db-old-primary "systemctl is-active mysql || systemctl is-active mariadb"
inactive
Що це означає: Сервіс MySQL/MariaDB зупинений на старому primary.
Рішення: Добре, але цього недостатньо, якщо хост може перезапуститись. Якщо у вас є автоматичні рестарти — вимкніть їх або ізолюйте вузол на мережевому рівні також.
Завдання 11: підтвердити, до кого фактично підключені клієнти (щоб впіймати брехню проксі)
cr0x@server:~$ mysql -N -e "SELECT SUBSTRING_INDEX(USER(),'@',-1) AS client_host, COUNT(*) FROM information_schema.processlist GROUP BY 1 ORDER BY 2 DESC LIMIT 5;"
10.12.4.31 87
10.12.4.18 54
10.12.4.22 41
Що це означає: Багато підключень з підмереж додатків — цей вузол отримує трафік.
Рішення: Якщо ви підвищили цей вузол, чудово. Якщо ні — виправляйте маршрути негайно і розгляньте, чи не відбувалися сюрпризні записи тут.
Завдання 12: на кандидаті зупинити реплікацію чисто перед підвищенням
cr0x@server:~$ mysql -e "STOP REPLICA;"
Query OK, 0 rows affected (0.02 sec)
Що це означає: Реплікація зупинена (синтаксис MySQL 8).
Рішення: Тепер ви можете безпечно оцінити стан і підвищити вузол, не дозволяючи йому продовжувати застосовувати події з потенційно мертвого джерела.
Завдання 13: зробити промотований вузол записуваним (і перевірити)
cr0x@server:~$ mysql -e "SET GLOBAL super_read_only=OFF; SET GLOBAL read_only=OFF;"
Query OK, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
cr0x@server:~$ mysql -N -e "SELECT @@read_only, @@super_read_only;"
0 0
Що це означає: Вузол записуваний.
Рішення: Робіть це лише після фензингу старого primary. Якщо не можете відфенсити, тримайте кандидата в read-only і переходьте у контрольований простій, поки не вирішите питання фензингу.
Завдання 14: перенаправити іншу репліку з автопозиціонуванням GTID (MySQL)
cr0x@server:~$ mysql -e "STOP REPLICA; CHANGE REPLICATION SOURCE TO SOURCE_HOST='db-new-primary', SOURCE_USER='repl', SOURCE_PASSWORD='***', SOURCE_AUTO_POSITION=1; START REPLICA;"
Query OK, 0 rows affected (0.01 sec)
Query OK, 0 rows affected (0.04 sec)
Query OK, 0 rows affected (0.01 sec)
Що це означає: Репліка тепер слідує за новим primary з позиціюванням на основі GTID.
Рішення: Негайно перевірте статус на помилки і відставання; якщо не наздоганяє — можливо errant transactions або очищені GTID.
Завдання 15: перенаправити MariaDB-репліку (класичний синтаксис і GTID)
cr0x@server:~$ mysql -e "STOP SLAVE; CHANGE MASTER TO MASTER_HOST='db-new-primary', MASTER_USER='repl', MASTER_PASSWORD='***', MASTER_USE_GTID=slave_pos; START SLAVE;"
Query OK, 0 rows affected (0.01 sec)
Query OK, 0 rows affected (0.05 sec)
Query OK, 0 rows affected (0.01 sec)
Що це означає: MariaDB-репліка перепрофільована для використання GTID, починаючи з її власної застосованої позиції.
Рішення: Якщо у повідомленнях про помилки реплікації згадується строгость GTID або дублікати, зупиніться і узгодьте позиції GTID; не робіть з «skip counter» звички.
Завдання 16: перевірити, що у нового primary увімкнено binlog (інакше репліки не підключаться)
cr0x@server:~$ mysql -N -e "SELECT @@log_bin;"
1
Що це означає: Бінлог увімкнено.
Рішення: Якщо це повертає 0, репліки не зможуть від нього реплікуватися. Ви або неправильно його налаштували, або підвищили хост, призначений лише для читання. Виправте конфіг і перезапустіть або оберіть інший кандидат.
Завдання 17: перевірити довгі транзакції, що блокують застосування (часто плутають з «відставанням реплікації»)
cr0x@server:~$ mysql -e "SHOW ENGINE INNODB STATUS\G" | egrep -n "TRANSACTIONS|LOCK WAIT|history list length" | head
3:TRANSACTIONS
45:---TRANSACTION 18446744073709551615, not started
112:History list length 987654
Що це означає: Дуже великий history list length може вказувати на затримку purge, довгі транзакції або інтенсивне навантаження на запис; це часто корелює з проблемами застосування реплікації.
Рішення: Перед підвищенням відсталої репліки з’ясуйте, чи вона відстає через IO-завантаження, очікування локів або purge. «Найбільш прогресивна, але перевантажена» репліка може впасти одразу після підвищення.
Завдання 18: підтвердити насичення диска (мовчазний вбивця відновлення)
cr0x@server:~$ iostat -x 1 3
Linux 5.15.0 (db-replica-2) 12/29/2025 _x86_64_ (16 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.10 0.00 4.20 38.50 0.00 45.20
Device r/s w/s rkB/s wkB/s await aqu-sz %util
nvme0n1 120.0 980.0 5200.0 42000.0 35.4 18.2 99.8
Що це означає: Диск завантажений (%util ~100), високий await. Застосування реплікації і записи від клієнтів потерпатимуть.
Рішення: Не підвищуйте цей хост, якщо є інший кандидат з кращим IO. Якщо це єдиний кандидат, обмежте навантаження, збільшіть запас безпеки буферного пулу і готуйтеся до повільного відновлення.
Три короткі історії з реального життя
Інцидент через неправильне припущення: «read_only означає немає записів»
У них була стандартна двовузлова конфігурація: один primary, одна репліка, плюс проксі. План відновлення був на одній сторінці: якщо primary помер, встановити старий primary в read_only=ON, підвищити репліку і переключити endpoint запису в проксі.
Настав день. Оновлення ядра перезавантажило primary несподівано. Моніторинг задзвонив, автоматика спрацювала, і репліка була підвищена. Проксі переключилося. Усі видихнули.
Потім primary повернувся. Systemd автоматично перезапустив сервіс бази. У проксі залишився застарілий connection pool до старого primary з дофейлових підключень, а деякі вузли додатку мали жорстко вбудовані fallback-хости. Існував привілейований користувач для «обслуговування», з достатніми правами, щоб обійти read_only у спосіб, який команда не розуміла. Записи просочувалися в старий primary кілька хвилин.
Коли їх пізніше переналаштували цей старий primary як репліку, він відмовився: існували errant transactions. Це — ввічливий режим відмови. Неввічливий — коли він приймає реплікацію і ви мовчки доставляєте сміття.
Виправлення не було «зробити read_only жорсткішим». Виправлення — фензинг: або повністю вимкнути старий primary і тримати його вимкненим, або ізолювати його на мережевому рівні так, щоб він не міг приймати клієнтський трафік. Також вони видалили «допоміжні хости» з конфігів додатків і зробили проксі єдиним джерелом істини.
Оптимізація, що обернулася проти: «зробимо коміти швидшими»
Команда ганялася за затримкою. У них був сервіс з великим обсягом записів і іноді спалахи відставання реплікації. Хтось запропонував знизити налаштування надійності: innodb_flush_log_at_trx_commit=2 та sync_binlog=0. Мовляв: «Ми можемо дозволити втратити секунду даних; у нас є реплікація».
Це працювало — поки не перестало. Primary впав під час проблем зі сховищем. База перезапустилася чисто, але останнє вікно «підтверджених» транзакцій фактично не було гарантовано на диску. Частину транзакцій, які були підтверджені клієнтам, не було в binlog і InnoDB redo.
Failover відбувся на найпросунутішу репліку. Додаток відновився. Потім інцидент перетворився на проблему відповідності, бо інші системи отримали підтвердження про події, яких тепер не існувало в БД. Звіт про помилку виглядав як змова: додаток мав логи «успіх», а база — жодного запису.
Вони відновили коректність, відтворивши події з зовнішньої черги і звіривши дані. Ніхто не отримав задоволення. Оптимізація не лише підвищила RPO; вона зробила відмови неінтуїтивними. Оце і є реальна вартість.
Компроміс: тримати на первинному вузлі надійні налаштування, працювати над індексацією і батчингом, інвестувати в semi-sync (з моніторингом його деградації). Тюнінг продуктивності, що змінює коректність, повинен бути продуктовим рішенням, а не швидким виграшем.
Нудна, але правильна практика, що врятувала день: «ми репетирували»
Інша компанія мала те, що здавалося зайвим: щомісячну вправу з відновлення, писаний runbook з гілками під кожен движок і невеликий скрипт, що робив read/write перевірки через той самий шлях, що й додаток.
Під час реального інциденту (мережева роздільність між зонами доступності) primary став недоступним для більшості додатків, але залишався доступним для декількох. Це прелюдія до split brain. Автоматизація зупинилася, бо health checks давали різні результати з різних точок — навмисно.
On-call дотримався playbook-а: відфенсив primary на мережевому рівні, потім підвищив найкращу репліку за GTID-станом і здоров’ям диска. Probe-скрипт перевірив, що «writer traffic» справді потрапляє на новий primary, а не тільки що TCP відкритий.
Аутейдж був не приємним, але коротким і контрольованим. Пізніше в постмортемі команда пишалася не «п’ятьма дев’ятками», а тим, що ніхто не сперечався щодо того, що сталося. Докази були чисті, бо вони відточували кроки збору доказів, а не лише самі кроки відновлення.
Репетиція нудна. Але це одне з небагатьох, що надійно перетворює теоретичний дизайн HA у практичний.
Поширені помилки: симптом → корінь → виправлення
1) Симптом: підвищення «успішне», але додаток все ще пише до старого primary
Корінь: відсутній фензинг; проксі все ще маршрутизує або додатки мають списки fallback-хостів; застарілий DNS/connection pool.
Виправлення: Фензьте старий primary (зупинка + ізоляція мережі), зробіть проксі єдиним endpoint-ом для запису і перевірте через write probe шлях додатка.
2) Симптом: репліки не підключаються до нового primary після підвищення
Корінь: підвищений вузол має log_bin=OFF, відсутні права replication user або невірний server_id.
Виправлення: Переконайтеся, що кандидати для підвищення збудовані з конфігом «готовим до підвищення»: увімкнений binlog, унікальний server_id, присутні користувачі реплікації, за потреби log_slave_updates.
3) Симптом: GTID-референсування не вдається з дублями або відсутніми GTID
Корінь: errant transactions на одній репліці, непослідовне очищення GTID або змішані режими GTID у флоті.
Виправлення: Стандартизуйте конфіг GTID у всіх вузлах. Під час інциденту оберіть кандидата, що є супермножиною виконаних транзакцій; перебудуйте неконсистентні репліки замість того, щоб примусово ставити їх у строй за допомогою skip.
4) Симптом: Seconds_Behind_* мале, але дані відсутні після відновлення
Корінь: цей метрик іноді брешуть (SQL-потік зупинено, паралельне застосування, дрейф годинника) або semi-sync вже впав у async.
Виправлення: Перевіряйте порівняння позицій GTID/binlog та сигнали від додатку про узгодженість. Моніторте лічильники semi-sync, а не лише конфігураційні прапори.
5) Симптом: відставання реплікації вибухає відразу після підвищення
Корінь: підвищений вузол вже був IO-навантажений, або буферний пул холодний, або довга транзакція блокує purge і спричиняє конкуренцію.
Виправлення: Обирайте кандидатів за здоров’ям (IO, CPU, метрики InnoDB), а не лише за «найактуальніші». Прогрівайте репліки, правильно налаштовуйте буферні пули і уникайте підвищення на вузол, що «плавиться».
6) Симптом: скрипт відновлення працює на MySQL, але падає на MariaDB (або навпаки)
Корінь: відмінності синтаксису (START REPLICA vs START SLAVE), відмінності змінних і семантики GTID.
Виправлення: Тримайте автоматику з гілками для кожного двигуна. Виявляйте двигун/версію перед виконанням. Обережно ставтеся до «достатньо сумісно» як до ризику, а не комфорту.
7) Симптом: після підвищення репліки вічно показують «connecting»
Корінь: не оновлені ACL/фаєрвол, неправильна адреса джерела або проксі/VIP переміщено, але репліка використовує прямі hostname-и.
Виправлення: Тримайте шляхи реплікації явними і протестованими. Використовуйте присвячені імена для реплікації, що маршрутизуються з підмереж реплік. Перевіряйте доступність через TCP-чек з підмереж реплік.
8) Симптом: SQL-потік репліки зупиняється з «DDL mismatch» або помилками метаданих
Корінь: змішані версії/SQL mode/колації; statement-based реплікація; або DDL, виконаний інакше.
Виправлення: Використовуйте row-based реплікацію. Стандартизуйте SQL mode і колацію. Уникайте failover між версіями, якщо можливо; якщо ні — протестуйте DDL-реплікацію перед продукцією.
Контрольні списки / покроковий план
Чекліст готовності до відновлення (зробіть перед тим, як це знадобиться)
- Фензинг існує і протестований: ви можете зупинити старий primary від прийому трафіку, навіть якщо він перезавантажиться.
- Конфіг для швидкого підвищення на репліках: увімкнений binlog, унікальний
server_id, присутні користувачі реплікації, правильні налаштування GTID. - Послідовна позиція щодо надійності: вирішіть, чи ви приймаєте втрату при краші; не залишайте це випадковістю через оптимізацію продуктивності.
- Row-based реплікація:
binlog_format=ROW, якщо на те немає вагомої причини. - Runbook з гілками для двигунів: враховані відмінності MySQL vs MariaDB і відточені в репетиціях.
- Сигнали здоров’я: застосування реплікації, затримка диска, метрики InnoDB, активність semi-sync, стан маршрутизації проксі.
- Інструмент write probe: мінімальний скрипт, що підтверджує, що новий писач дійсно приймає записи по реальному шляху клієнта.
План при інциденті (контрольована версія)
- Зупиніть кровотечу: призупиніть автоматику, зменшіть записувальне навантаження, по можливості заморозьте зміни схеми.
- Фензьте старий primary: зупинка + ізоляція мережі. Переконайтеся, що він недоступний клієнтам.
- Виберіть кандидата: порівняйте позиції GTID/binlog; відкиньте репліки з SQL-помилками або дрейфом; перевірте IO-здоров’я.
- Зупиніть реплікацію на кандидатові: запобіжте подальшому застосуванню з потенційно скомпрометованого джерела.
- Підвищення: вимкніть read-only; переконайтеся, що binlog увімкнено; перевірте, що вузол приймає записи.
- Перемістіть трафік: оновіть проксі/VIP/DNS; перевірте через write probe; спостерігайте за рівнем помилок.
- Перепідключіть репліки: використовуйте GTID де можливо; перевіряйте, що кожна репліка наздоганяє і лишається стабільною.
- Післяфейловерне прибирання: перебудуйте зламані репліки, перевірте бекапи і задокументуйте спостереження.
Коли обирати MySQL vs MariaDB для середовищ з великою кількістю failover
Оберіть MySQL (особливо 8.0+), якщо:
- Ви хочете чітку «єдиного вендора» HA-історію з Group Replication/InnoDB Cluster як опцією.
- Ваші інструменти та знання персоналу вже орієнтовані на MySQL (Orchestrator, MySQL shell, семантика 8.0).
- Ви цінуєте передбачувану поведінку GTID-наборів і загальну оперативну послідовність у хостингових пропозиціях.
Оберіть MariaDB, якщо:
- Ви інвестували в екосистемні інструменти MariaDB, як MaxScale, і добре розумієте її поведінку під час відновлення.
- Ви отримуєте вигоду від специфічних функцій MariaDB і готові розглядати GTID і відновлення як нативні для MariaDB, а не «MySQL-подібні».
- Ваша інфраструктура однорідна під MariaDB достатньо, щоб уникнути пасток сумісності.
Уникайте змішаного двигуна для failover, якщо тільки ви не протестували точні версії, режим GTID, налаштування binlog і поведінку інструментів під час відмови. «Воно реплікується в staging» — це не доказ; це початкова чутка.
FAQ
1) Чи можу я переключатися між репліками MySQL і MariaDB?
Іноді можна реплікувати між ними в певних комбінаціях версій і налаштувань, але failover ризикований. Семантика GTID різниться, і інструменти часто припускають один двигун. Якщо потрібно, тримайте писача в одній сім’ї двигуна і розглядайте інший лише як споживача лише для читання, а не як кандидат на підвищення.
2) Чи гарантує GTID відсутність втрати даних під час відновлення?
Ні. GTID допомагає ідентифікувати, що де виконано, і спрощує перепідключення реплік. Втрата даних залежить від того, які транзакції були підтверджені на старому primary до його падіння і чи отримали/застосували їх репліки.
3) Чи достатньо semi-sync, щоб гарантувати нульову втрату даних?
Сам по собі — ні. Semi-sync може переключитися на async під навантаженням або через мережеві проблеми. Треба моніторити, чи він був активний під час збою, і знати налаштування вимог підтвердження.
4) Чому «Seconds_Behind_Master» іноді бреше?
Бо це оцінка, заснована на часових мітках і стані потоків. Якщо SQL-потік зупинено, relay log-и затримуються, годинники дрейфують, або йде паралельне застосування, число може вводити в оману. Використовуйте порівняння GTID/binlog позицій і статус помилок як основні сигнали.
5) Який є найважливіший контроль відновлення?
Фензинг. Якщо ви не можете гарантувати, що старий primary не прийматиме записи, усе інше — театр.
6) Використовувати statement-based чи row-based реплікацію для failover?
Row-based. Statement-based і змішані формати створюють недетермінований дрейф і залежність від версій. Failover — це момент, коли дрейф стає помітним і дорогим.
7) Які інструменти використовувати для відновлення?
Використовуйте інструмент, що розуміє ваш двигун і режим GTID, і підтримує фензинг або інтегрується з чимось, що може фензити (або хоча б призупиняти підвищення, поки людина не підтвердить фензинг). Orchestrator поширений у MySQL-флітах; MaxScale — у MariaDB; ProxySQL може працювати як шар маршрутизації, але потребує коректної логіки health check.
8) Як обрати найкращу репліку для підвищення?
Оберіть репліку з (a) найповнішим набором виконаних/застосованих транзакцій, (b) без помилок реплікації, і (c) хорошим здоров’ям хоста (затримка диска, відновлення після збою не триває). «Найпередовіша, але перевантажена» — пастка.
9) Чому підвищення іноді провалюється через «errant transactions»?
Бо репліка виконала транзакції, відсутні у стрімі primary (ручні записи, погана автоматика або попередній split brain). GTID робить це видимим. Виправлення — перебудова або хірургічне узгодження — зазвичай перебудова.
10) Як часто проводити репетиції відновлення?
Щомісяця, якщо можете, щокварталу — якщо мусите. Репетиція — це спосіб дізнатися, що ваш проксі кешує DNS 10 хвилин або що ваш «фензинг» насправді ввічлива прохань.
Висновок: подальші кроки на цей тиждень
Відновлення після відмови ламається не тому, що MySQL чи MariaDB «погані». Воно ламається, бо ви покладалися на припущення, які ніколи не перевіряли в умовах відмови. Двигуни різняться в семантиці GTID, поверхні команд реплікації і очікуваннях інструментів екосистеми. Ці відмінності керовані — якщо ви їх визнаєте.
Зробіть наступне, у порядку:
- Запишіть ваш контракт відновлення: прийнятний RPO/RTO, хто вирішує про втрату даних (якщо потрібно), і що означає «успіх» (не «база вгору», а «записи йдуть в одне місце»).
- Реалізуйте справжній фензинг: не
read_only. Щось, що зупиняє записи на старому primary навіть якщо він перезавантажиться. - Стандартизуйте режим реплікації: row-based, узгоджений SQL mode/колація, послідовні GTID-настройки за двигуном.
- Збудуйте профіль репліки, готової до підвищення: увімкнений binlog, унікальний
server_id, присутні користувачі та привілеї реплікації, і моніторинг. - Проведіть репетицію відновлення і збирайте докази так, як у реальному інциденті: статуси, IO-статистику, стан проксі. Виправте першу несподіванку, яку знайдете. Несподіванок буде багато.
Перефразована ідея від John Allspaw: надійність походить з постійного навчання, а не з вдавання, що системи передбачувані під тиском
.
Жарт №2: Єдине, що більш оптимістичне, ніж «відновлення автоматичне», — це «runbook актуальний».