Оновлення MySQL та MariaDB: як оновити без руйнування продакшену

Було корисно?

Якщо ви коли-небудь «швидко оновлювали» базу даних, а потім спостерігали, як ваш застосунок утихомирено й ввічливо впадає в паніку — збільшується затримка, з’являються помилки, репліки відстають — ви вже знаєте висновок: оновлення рідко «гучно» ламаються; вони ламаються винахідливо.

Це посібник для продакшену. Не теорія. Не маркетинг вендора. Мета проста: просунути MySQL або MariaDB вперед (або перейти між ними), зберігаючи дані в безпеці, затримки в розумних межах і відкат правдоподібним.

Виберіть шлях: поетапне, ін-плейс або міграція

Більшість оновлень баз даних йдуть не так з трьох причин: ви вибрали неправильний метод оновлення, пропустили репетицію або обманули себе щодо відкату.

Варіант A: Ін-плейс мажорне оновлення (найшвидше, найризикованіше)

Ін-плейс означає, що ви зупиняєте MySQL/MariaDB на вузлі і перезапускаєте його з новою версією, вказуючи на ту саму datadir. Це може бути прийнятно для невеликих некритичних систем. У продакшені зазвичай це останній вибір, бо:

  • Відкат часто не є відкатом. Після оновлення файлів даних повернення може означати «відновлення з бекапу і молитва».
  • Примусово потрібний простій.
  • Якщо ви виявите регресію продуктивності, ви будете дебажити під тиском часу.

Якщо мусите робити ін-плейс, робіть це спочатку на репліці. Якщо у вас немає реплік, перший крок оновлення — побудувати реплікацію.

Варіант B: Поетапне оновлення з використанням реплікації (рекомендовано для більшості)

Це дорослий шлях. Ви оновлюєте репліки спочатку, перевіряєте, а потім виконуєте перемикання. Ваш простій перетворюється на контрольоване вікно перемикання, а не на екзистенційний випадок.

  • Краще для: асинхронної реплікації MySQL, semi-sync, реплікації MariaDB, багатьох сценаріїв Galera (з обмеженнями версій).
  • Відкат має правдоподібність: можна повернутися до старого primary (за умови, що ви його зберегли і реплікація залишається сумісною).
  • Ризик зміщується від «формату файлів даних» до «сумісності реплікації та поведінки запитів». Це кращий тип ризику.

Варіант C: Міграція через логічний дамп/відновлення або CDC (найповільніше, найчистіше)

Якщо ви змінюєте рушій (MySQL → MariaDB або MariaDB → MySQL), або потрібно перейти через несумісні мажорні версії, можливо варто зробити:

  • Логічно: mysqldump / mydumper, відновлення в новий кластер, переключення.
  • CDC: реплікація на основі binlog (нативна) або конвеєр change data capture, що стрімить зміни в новий кластер.

Логічна міграція повільна, але детермінована. CDC елегантний, але операційно важчий. В обох випадках ви отримуєте чистий старт і менше сюрпризів із форматом файлів.

Правило по досвіду: якщо бізнесу важливо, використовуйте поетапні оновлення або CDC-міграцію. Ін-плейс у продакшені — це спосіб дізнатися справжнє значення «незаплановане обслуговування».

Кілька фактів і історія (те, що ще кусає)

Оновлення простіші, коли ви розумієте, чому екосистема виглядає саме так. Ось конкретні факти, які й досі мають оперативне значення:

  1. MariaDB відгалузилась від MySQL у 2009 році після того, як Oracle придбала Sun Microsystems. Це розділення породило два різні плани розвитку з «переважно сумісні» як рухома ціль.
  2. MySQL 5.7 → 8.0 — це не «звичайне» мажорне оновлення. Оптимізатор, словник даних, значення за замовчуванням і аутентифікація змінилися так, що це проявляється як помилки застосунку, а не лише як робота DBA.
  3. MySQL 8.0 перемістив метадані в транзакційний словник даних (на базі InnoDB). Операційно: менше дивних .frm-епохальних артефактів, але оновлення переписують внутрішні структури і можуть займати реальний час.
  4. MariaDB зберегла Aria як рушій системних таблиць у деяких місцях і розвивала функції незалежно (наприклад, інша поведінка JSON і функції оптимізатора). Ця незалежність дає силу — і ризик сумісності.
  5. «JSON» — чудовий приклад розбіжності: JSON у MySQL — бінарний формат з функціями й індексами під нього; MariaDB історично трактувала JSON як псевдонім TEXT, а функції JSON розвивалися інакше.
  6. Плагіни аутентифікації змінили очікування у MySQL 8.0 (за замовчуванням caching_sha2_password). Клієнти і проксі, які «працювали роками», раптом можуть перестати працювати.
  7. Реалізації GTID відрізняються між MySQL і MariaDB. Не можна просто «увімкнути GTID» і без підготовки переходити між ними.
  8. MySQL видалив query cache роками тому (8.0), а MariaDB зберігав його довше. Якщо ви випадково покладалися на поведінку query cache, оновлення будуть виглядати як загадкова втрата продуктивності.
  9. Розмір сторінки InnoDB, налаштування redo log і поведінка відновлення після збою можуть змінювати значення за замовчуванням і евристики між версіями. Ваше RTO може змінитися без погодження.

Жарт №1: Єдиний «один дивний трюк» у оновленнях баз даних — це зробити бекап, який ви фактично відновлювали хоча б раз.

Карта сумісності: що ламається між MySQL і MariaDB

«Сумісний» — це не бінарна властивість. Це список речей, які рано чи пізно нашкодять у продакшені, з прикріпленим часовим штампом.

MySQL → MySQL (та сама сім’я, але все одно небезпечно)

  • SQL-мод і значення за замовчуванням: зміни в поведінці можуть проявитися як помилки обрізання даних або «раптом строгі» вставки.
  • Регресії оптимізатора: той самий запит може вибрати інший план і стати повільним під навантаженням.
  • Аутентифікація та TLS: бібліотеки клієнта, HAProxy/ProxySQL і старі JDBC-версії можуть не підключатися.
  • Фільтрація реплікації та метадані: дрібні конфігураційні відмінності можуть зламати реплікацію під час переключення.

MariaDB → MariaDB

  • Обмеження версій Galera: не можна просто поетапно оновити будь-яку мажорну версію через кластер. Перевірте підтримувані шляхи оновлення; інакше це реконструкція кластера.
  • Зміни системних таблиць: mysql.* таблиці та схема привілеїв еволюціонують; оновлення можуть вимагати явних пост-кроків.
  • Історія InnoDB vs XtraDB: старі версії MariaDB мали відмінні гілки коду рушія збереження; сучасні версії ближчі, але спадщина все ще існує.

MySQL ↔ MariaDB (зміна рушія)

Саме тут «запустилось нормально» перетворюється на пастку.

  • Формат файлів даних і системні таблиці відрізняються: ін-плейс заміна бінарників — не стратегія; це азартна гра.
  • Різниці GTID: міграція налаштувань реплікації між ними вимагає ретельного проектування (і часто пересіву даних).
  • SQL-функції розходяться: підтримка функцій вікон, JSON-функції, підказки оптимізатора та зарезервовані слова відходять у різні боки.
  • Очікування клієнтів: поведінка конекторів, значення за замовчуванням аутентифікації та версії TLS відрізняються між дистрибуціями.

Правило на підставі досвіду: розглядайте MySQL ↔ MariaDB як міграцію, а не як «онова‑лення». Плануйте паралельну роботу, валідацію і переключення з вікном для відкату.

Плейбук швидкої діагностики: що перевірити першим/другим/третім

Коли оновлення пішло боком, вам не потрібна 40-вкладкова сесія в браузері. Потрібна послідовність тріажу, яка скаже, де живе час і біль.

Перше: CPU, IO чи блокування?

  • CPU-зв’язане: регресія плану, відсутні індекси, збільшення сортувань/тимчасових таблиць або накладні витрати TLS/аутентифікації при високому числі відкритих з’єднань.
  • IO-зв’язане: змінена поведінка флашингу, тиск doublewrite, зміни в redo логах, занадто малий innodb_buffer_pool_size, стрибок затримки сховища.
  • Блокування: метадані‑блоки, нова поведінка DDL, довгі транзакції або затори потоку застосування репліки.

Друге: чи здорова і порівняна реплікація?

  • Відставання репліки та помилки застосування після оновлення — ранні сигнали. Їх часто ігнорують до переключення. Не ігноруйте.
  • Підтвердьте формат binlog, режим GTID (якщо використовується) і статус SQL-потоку репліки.

Третє: чи змінився спосіб роботи застосунку?

  • Репулювання пулу підключень через невідповідність плагіну аутентифікації і цикли повторного підключення.
  • Нова строгість, що викликає повторні спроби.
  • Зміни часових поясів, сортування або набору символів, що дають тонкі відмінності та промахи кешів.

Четверте: чи вціліла система спостереження після оновлення?

  • Налаштування Performance Schema, формат slow log і назви метрик можуть змінитись. Якщо дашборди потемніли, ви дебажите в темряві.

Найкращий один крок під тиском: порівняйте один відомий дорогий запит на старому та новому з тим самим набором даних і параметрами. Якщо план відрізняється — у вас є напрямок для розслідування.

Жарт №2: «Ми нічого не міняли» — еквівалент «собака з’їла моє домашнє» для баз даних, тільки собака тут оптимізатор.

Задачі в продакшені: команди, виводи та рішення (12+)

Ось задачі, які можна виконати на реальних серверах. Кожна включає: команду, правдоподібний фрагмент виводу, що це означає і рішення, яке ви приймаєте.

Задача 1: Визначити точну версію сервера та дистрибуцію

cr0x@server:~$ mysql -e "SELECT VERSION() AS version, @@version_comment AS distro, @@version_compile_machine AS arch\G"
*************************** 1. row ***************************
version: 8.0.36
distro: MySQL Community Server - GPL
arch: x86_64

Значення: Ви не здогадуєтеся. «MySQL 8» — це не версія; 8.0.36 — це версія. MariaDB покаже інші коментарі.

Рішення: Явно зафіксуйте цільові версії для оновлення. Якщо ви не можете назвати поточні та цільові патч‑версії — ви не плануєте оновлення; ви плануєте сюрприз.

Задача 2: Перевірити критичні відмінності в конфігурації (ефективні під час роботи)

cr0x@server:~$ mysql -e "SHOW VARIABLES WHERE Variable_name IN ('datadir','log_bin','binlog_format','gtid_mode','enforce_gtid_consistency','innodb_buffer_pool_size','innodb_flush_log_at_trx_commit','sync_binlog','sql_mode','character_set_server','collation_server')"
+------------------------------+---------------------------+
| Variable_name                | Value                     |
+------------------------------+---------------------------+
| binlog_format                | ROW                       |
| character_set_server         | utf8mb4                   |
| collation_server             | utf8mb4_0900_ai_ci        |
| datadir                      | /var/lib/mysql/           |
| enforce_gtid_consistency     | ON                        |
| gtid_mode                    | ON                        |
| innodb_buffer_pool_size      | 34359738368               |
| innodb_flush_log_at_trx_commit| 1                        |
| log_bin                      | ON                        |
| sql_mode                     | ONLY_FULL_GROUP_BY,...    |
| sync_binlog                  | 1                         |
+------------------------------+---------------------------+

Значення: Це ваша операційна особистість: надійність, формат реплікації, строгість і обробка символів.

Рішення: Будь‑яке оновлення/міграція має зберегти наміри. Якщо нова версія змінює значення за замовчуванням — явним чином перевизначайте їх у конфігу, а не сподівайтеся.

Задача 3: Підтвердити запас вільного місця на диску перед оновленням

cr0x@server:~$ df -h /var/lib/mysql
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p2  900G  720G  135G  85% /var

Значення: Мажорні оновлення можуть створювати тимчасові файли, перебудовувати внутрішні структури й збільшувати redo/undo під час догоняння.

Рішення: Менше ~20% вільного місця — червоний прапор. Розширте сховище або плануйте міграцію на новий том. «Ми впораємося» — це не стратегія для файлової системи.

Задача 4: Шукати пошкоджені таблиці або попередження InnoDB

cr0x@server:~$ sudo tail -n 60 /var/log/mysql/error.log
2025-12-29T02:14:11.102334Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
2025-12-29T02:15:03.981245Z 0 [Warning] [MY-012639] [InnoDB] Retry attempts for reading partial data failed.
2025-12-29T02:15:04.001103Z 0 [Note] [MY-010431] [Server] Detected data dictionary initialization

Значення: Оновлення підсилюють існуючі проблеми. Якщо InnoDB вже скаржиться — не додавайте ризиків зверху.

Рішення: Розслідуйте і виправте проблеми зі сховищем/логами спочатку (SMART/NVMe помилки, помилки файлової системи, повідомлення ядра). Якщо потрібно — зробіть свіжий логічний бекап.

Задача 5: Виміряти здоров’я реплікації на репліках (перш ніж торкатися чогось)

cr0x@server:~$ mysql -e "SHOW REPLICA STATUS\G" | egrep "Replica_IO_Running|Replica_SQL_Running|Seconds_Behind_Source|Last_SQL_Error|Retrieved_Gtid_Set|Executed_Gtid_Set"
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Seconds_Behind_Source: 0
Last_SQL_Error:
Retrieved_Gtid_Set: 2f9c3b3a-...:1-98233411
Executed_Gtid_Set: 2f9c3b3a-...:1-98233411

Значення: Репліка, яка сьогодні не може реплікувати, не стане магічно працюючою після оновлення.

Рішення: Виправте відставання реплікації спочатку. Не оновлюйте поверх несправної реплікації; ви втратите найпростіший шлях для відкату.

Задача 6: Перевірити довгі транзакції (вбивці оновлень і перемикань)

cr0x@server:~$ mysql -e "SELECT trx_id, trx_started, TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) AS age_s, trx_mysql_thread_id, trx_query FROM information_schema.innodb_trx ORDER BY age_s DESC LIMIT 5\G"
*************************** 1. row ***************************
trx_id: 145922993
trx_started: 2025-12-29 01:47:12
age_s: 2081
trx_mysql_thread_id: 17322
trx_query: UPDATE orders SET status='PAID' WHERE id=...

Значення: Довгі транзакції блокують очистку, можуть блокувати DDL і роблять перемикання дуже довгим (а репліки відстають).

Рішення: Перед cutover встановіть правило «немає довгих транзакцій»: призупиніть пакетні джоби, виправте застряглих записувачів і розгляньте встановлення max execution time там, де це доцільно.

Задача 7: Знайти топ чекань (блокування vs IO vs CPU) за допомогою Performance Schema

cr0x@server:~$ mysql -e "SELECT event_name, COUNT_STAR, ROUND(SUM_TIMER_WAIT/1000000000000,2) AS total_s FROM performance_schema.events_waits_summary_global_by_event_name WHERE SUM_TIMER_WAIT > 0 ORDER BY SUM_TIMER_WAIT DESC LIMIT 8"
+------------------------------------------+------------+---------+
| event_name                               | COUNT_STAR | total_s |
+------------------------------------------+------------+---------+
| wait/io/table/sql/handler                | 192233331  | 8421.33 |
| wait/synch/mutex/innodb/buf_pool_mutex   |  98223311  | 3120.10 |
| wait/io/file/innodb/innodb_data_file     |  12233411  | 1777.54 |
| wait/lock/table/sql/handler              |   2233111  |  601.22 |
+------------------------------------------+------------+---------+

Значення: Ви дивитесь, на що сервер витрачає час у чеканні. Не на відчуття. Не на Slack.

Рішення: Якщо домінують IO‑очікування — плануйте валідацію сховища і налаштувань флашингу. Якщо домінують блокування — аудитуйте запити і межі транзакцій перед оновленням.

Задача 8: Порівняти плани запитів до оновлення з EXPLAIN ANALYZE

cr0x@server:~$ mysql -e "EXPLAIN ANALYZE SELECT * FROM sessions WHERE user_id=123 AND created_at > NOW() - INTERVAL 7 DAY ORDER BY created_at DESC LIMIT 50\G"
*************************** 1. row ***************************
EXPLAIN: -> Limit: 50 row(s)  (actual time=0.321..0.338 rows=50 loops=1)
    -> Index range scan on sessions using idx_user_created (user_id=123)  (actual time=0.320..0.333 rows=50 loops=1)

Значення: Це ваш правдивий тест. Якщо «actual time» або метод доступу зміняться після оновлення — ви знайшли регресію.

Рішення: Захопіть базовий набір критичних запитів і їх плани. Після оновлення репліки порівняйте. Якщо плани різняться — виправляйте індексами, підказками (обережно) або переписом запитів перед cutover.

Задача 9: Перевірити дрейф набору символів/колації, що ламає індекси й порівняння

cr0x@server:~$ mysql -e "SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CHARACTER_SET_NAME, COLLATION_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='app' AND DATA_TYPE IN ('varchar','text','char') AND COLLATION_NAME NOT LIKE 'utf8mb4%' LIMIT 10"
+--------------+------------+-------------+--------------------+-------------------+
| TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME | CHARACTER_SET_NAME | COLLATION_NAME    |
+--------------+------------+-------------+--------------------+-------------------+
| app          | users      | email       | latin1             | latin1_swedish_ci |
+--------------+------------+-------------+--------------------+-------------------+

Значення: Змішані колації викликають дивні порядки, порушення унікальності й повільні порівняння.

Рішення: Не змінюйте колації під час оновлення рушія, якщо ви не готові до проєкту міграції схеми. Якщо треба — робіть це окремо з власним радіусом впливу і планом відкату.

Задача 10: Перевірити використання застарілих/видалених фіч

cr0x@server:~$ mysql -e "SHOW VARIABLES LIKE 'query_cache%';"
Empty set (0.00 sec)

Значення: У MySQL 8.0 змінні query cache відсутні. У MariaDB вони можуть бути в залежності від версії.

Рішення: Якщо застосунок випадково використовував query cache, після оновлення зросте навантаження. Підготуйте ємність і перенесіть кешування на рівень застосунку, де воно має належати.

Задача 11: Перевірити сумісність клієнтів з плагінами аутентифікації

cr0x@server:~$ mysql -e "SELECT user, host, plugin FROM mysql.user WHERE user IN ('app','replicator')\G"
*************************** 1. row ***************************
user: app
host: %
plugin: caching_sha2_password
*************************** 2. row ***************************
user: replicator
host: 10.0.0.%
plugin: mysql_native_password

Значення: Якщо бібліотека клієнта не підтримує caching_sha2_password, підключення не вдасться або відбудеться цикл повторних підключень і ви отримаєте ввічливий DoS.

Рішення: Або оновіть клієнтів/конектори/проксі перед оновленням сервера, або явно встановіть плагін користувача. Надавайте перевагу оновленню клієнтів; пониження аутентифікації — це компроміс у безпеці, який треба робити свідомо.

Задача 12: Підтвердити, що бінлог і утримання binlog не вибухнуть під час міграції

cr0x@server:~$ mysql -e "SHOW BINARY LOGS;"
+------------------+-----------+
| Log_name         | File_size |
+------------------+-----------+
| binlog.000331    |  10485776 |
| binlog.000332    | 1073741824|
| binlog.000333    | 1073741824|
+------------------+-----------+

Значення: Ваші binlog великі і часті. Під час репетицій cutover і відновлення реплік churn binlog може заповнити диски.

Рішення: Забезпечте запас місця та встановіть розумний термін зберігання binlog. Для MySQL 8.0 віддавайте перевагу binlog_expire_logs_seconds. Уникайте агресивного зменшення часу зберігання під час оновлення, якщо не любите перебудовувати репліки.

Задача 13: Зробити логічний бекап, який ви реально зможете відновити (перевірка відновлення)

cr0x@server:~$ mysqldump --single-transaction --routines --events --triggers --hex-blob --set-gtid-purged=OFF --databases app > /backups/app.sql
cr0x@server:~$ mysql -e "CREATE DATABASE restore_test; USE restore_test; SOURCE /backups/app.sql;"
Query OK, 1 row affected (0.01 sec)

Значення: Бекап існує і його можна використати. Це трапляється рідше, ніж має.

Рішення: Якщо відновлення не вдається — зупиніться. Виправте процес бекапу/відновлення перед тим, як торкатися продакшен‑бінарників. Бекапи — не галочка; це ваш відкат, коли все інше горить.

Задача 14: Прогнати додаток проти тіньової інстанції

cr0x@server:~$ mysql -e "SET GLOBAL log_output='TABLE'; SET GLOBAL slow_query_log=ON; SET GLOBAL long_query_time=0.2;"
Query OK, 0 rows affected (0.00 sec)

Значення: Ви можете захоплювати повільні запити на оновленій репліці, запускаючи дзеркальний читальний трафік (або синтетичні тести).

Рішення: Якщо оновлена репліка показує нові повільні запити для того самого робочого навантаження — не перемикайтеся, поки не зрозумієте причину. «Напевно, все добре» — шлях до тижневого інциденту.

Три корпоративні міні-історії з окопів

Інцидент через неправильне припущення: «реплікація — це реплікація»

Вони керували завантаженою SaaS‑платформою з простою асинхронною реплікацією: один primary, дві репліки, нічні бекапи. План був «модернізувати» систему, перейшовши з MariaDB на MySQL, бо інструмент постачальника «надавав перевагу MySQL 8». Документ міграції був на два аркуші. Це мало бути підказкою.

Неправильне припущення було тонким: вони думали, що їхній підхід з GTID у MariaDB прямо відобразиться в MySQL. Команда побудувала нову MySQL‑репліку і спробувала приєднати її до MariaDB primary, і виявила, що «GTID» — це не єдиний стандарт між форками. Вони перейшли на реплікацію по файлу/позиції, що більш-менш працювало, поки тест переключення не виявив розбіжність по позиціях binlog і репліка не почала відхиляти події.

Під час cutover застосунок переключив записи на новий MySQL primary, а стара MariaDB повинна була залишитись як план відкату. Але для відкату потрібна була зворотна реплікація, щоб утримувати її синхронною. Цей зворотний шлях не був стабільним. Через годину вони мали дві розійшлися реальності і жодної чистої лінії назад.

Виправлення не було гламурним. Вони перебудували міграцію як CDC‑конвеєр з явними перевірками консистентності і перестали намагатися тлумачити GTID однаково між рушіями. Також ввели жорстке правило: відкат повинен бути протестований під реальним записуючим трафіком у репетиції, а не «здається правдоподібним». Платформа вижила. Дво‑сторінковий документ — ні.

Оптимізація, що обернулась проти: «давайте підлаштуємо флашинг під час оновлення»

Інша компанія мала флот MySQL 5.7 на швидких NVMe. Під час планованого оновлення до 8.0 хтось запропонував тимчасово послабити налаштування надійності, щоб прискорити догоняння і зменшити час перемикання. Зокрема, вони знизили innodb_flush_log_at_trx_commit і sync_binlog на оновлених репліках.

У стенді це виглядало чудово. Реплікація застосовувалася швидше; запити були швидкими. У продакшені була додаткова змінна: періодичні проблеми з живленням в одному стійці і глюк прошивки контролера сховища, які були незначними при строгому флашингу. Через два дні одна оновлена репліка впала. Відновлення після збою завершилось, але воно програло журнали трохи інакше, ніж очікувалось, і з’явилась невідповідність в одній краєвій таблиці, що використала суміш автокоммітів і спадкового тригера.

Дані в сенсі «екзистенційної втрати» не зникли — більшість таблиць була в порядку — але невідповідність блокувала промоцію цієї репліки і змусила перебудовувати її під час релізу. Загальне оновлення дуже сповільнилось. «Оптимізація» додала більше ризику простого часу, ніж забрала.

Урок: не вводьте налаштування, що торкаються надійності, під час оновлення, якщо ви не готові дебагати їх як виробничі фічі. Якщо хочете змінити такі параметри — робіть це окремо, з власним вікном тестування, і свідомо приймайте ризики.

Перефразована ідея, часто приписувана Werner Vogels: «Все ламається; проектуйте і експлуатуйте, передбачаючи це». Це стосується і ваших хитрих налаштувань під час оновлення.

Нудна, але правильна практика, що врятувала день: репетиція cutover з реальним відкатом

Одна команда в підприємстві мала звичку, через яку їх підколювали: вони проводили повні генеральні репетиції оновлень бази даних, включно з відкатом. Не «у нас є бекапи», а «ми практикували повернення під навантаженням». Це здавалося надмірним — поки не стало в пригоді.

Вони оновлювали MariaDB у три‑нодовому наборі для внутрішньої фінансової системи. Під час репетиції #1 вони виявили, що інструмент міграції схеми відкриває довгі транзакції, що спричиняє метадані‑блокування під час перемикання. Це перетворило б 2‑хвилинний cutover у 45‑хвилинний простій. Вони виправили конфігурацію інструмента і розділили зміни схеми від оновлення рушія.

Під час репетиції #2 вони виявили, що агент моніторингу використовує застарілу статусну змінну і перестає показувати латентність реплікації на оновлених вузлах. Вони оновили дашборди і алерти до продакшену.

У реальну ніч оновлення пройшло добре — поки зміна ACL в іншому місці не викликала періодичну втрату пакетів до однієї репліки. Команда миттєво побачила джиттер реплікації (бо моніторинг ще працював), призупинила промоцію і переключилась на іншу оновлену репліку. Жодного інциденту. Нудна робота з репетиціями зробила всю драматичну роботу за них.

Чеклісти / поетапний план

Тут ховається два проєкти в одному: зміна рушія бази даних і зміна операційна. Віднесіться до обох серйозно.

Фаза 0: Визначте, що саме ви робите

  • Той самий рушій, мажорна версія (MySQL 5.7→8.0, MariaDB 10.x→11.x): використовуйте поетапне оновлення де можливо.
  • Зміна рушія (MySQL ↔ MariaDB): плануйте міграцію з валідацією. Припускайте несумісності, поки не доведено протилежне.
  • Galera: перевірте підтримувані шляхи поетапного оновлення. Якщо не підтримується — плануйте побудову нового кластера і cutover.

Фаза 1: Перевірка перед польотом (зробіть за тиждень до)

  • Інвентар: версії, конфіги, плагіни, використання рушіїв збереження.
  • Базова лінія: p95/p99 затримка запитів, топ‑запити, відставання реплікації, hit rate буфера, тиск redo/undo.
  • Бекапи: тест відновлення повного логічного бекапу; тест відновлення фізичного бекапу, якщо ви його використовуєте.
  • Сумісність клієнтів: конектори, проксі, TLS, плагіни аутентифікації.
  • Запас місця на диску та IOPS: оновлення спричинюють сплески.
  • Визначте успіх: прийнятний рівень помилок, затримки і тривалість cutover.

Фаза 2: Побудуйте середовище репетиції (не пропускайте)

Використовуйте дані, схожі на продакшен. Якщо не можете — як мінімум використовуйте продакшен‑схему і відтворення навантаження.

  • Відновіть останній нічний бекап у стенді.
  • Прикріпіть лише для читання відтворення навантаження або синтетичний бенчмарк, що приблизно відповідає міксу запитів.
  • Запустіть інтеграційні тести застосунку проти нової версії.
  • Порівняйте плани запитів для критичних запитів.

Фаза 3: План поетапного оновлення (на основі реплікації)

Крок 1: Оновіть одну репліку

  • Зупиніть реплікацію (або тримайте її запущеною залежно від методу), оновіть бінарник, виконайте потрібні пост‑кроки після оновлення.
  • Дайте їй догнатися, перевірте швидкість застосування і затримку запитів.

Крок 2: Глибоко валідуйте перед тим, як торкатися primary

  • Запустіть лише читальний трафік проти оновленої репліки (або дзеркальні читання) і порівняйте помилки/затримки.
  • Підтвердіть, що реплікація стабільна протягом годин, а не хвилин.
  • Підтвердіть, що бекапи працюють на новій версії (інструменти іноді змінюють поведінку).

Крок 3: Оновіть інші репліки

Ніколи не оновлюйте всі репліки одночасно, якщо можете цього уникнути. Залиште хоча б одну відому‑добру репліку на старій версії до успішного і стабільного cutover.

Крок 4: Перехід (Cutover)

  • Коротко зупиніть записи, якщо ваш процес перемикання цього вимагає.
  • Промотуйте оновлену репліку до primary.
  • Перенаправте застосунок, швидко перевірте, потім поступово увімкніть фоні джоби.

Крок 5: План відкату (ви його практикуєте)

  • Зберігайте старий primary у цілості протягом визначеного вікна для відкату.
  • Визначте тригери для відкату (затримка, рівень помилок, нестабільність реплікації).
  • Майте скриптовану, протестовану процедуру для перенаправлення трафіку назад.

Фаза 4: План міграції (MySQL ↔ MariaDB)

Якщо ви змінюєте рушій, віддавайте перевагу побудові нового кластера і контрольованому cutover:

  • Забезпечте новий кластер з цільовим рушієм/версією.
  • Завантажте базові дані через фізичне відновлення (якщо сумісно) або логічне відновлення.
  • Стрійте зміни, використовуючи реплікацію/CDC (залежно від рушія).
  • Валідуйте контрольними сумами і тіньовими читаннями застосунку.
  • Переключіть записи; збережіть старий кластер у режимі лише для читання для вікна відкату.

Звичайні помилки: симптом → корінь → фікс

1) Симптом: оновлення пройшло, але застосунок не може підключитися

Корінь: невідповідність плагіну аутентифікації (наприклад, caching_sha2_password у MySQL 8) або невідповідність TLS/шифрів у проксі/клієнтах.

Фікс: оновіть конектори/проксі спочатку; перевірте на канарці. Якщо екстрено — встановіть плагін користувача mysql_native_password для цього користувача і заплануйте правильне виправлення.

2) Симптом: раптовий стрибок затримки запитів, при тому ж навантаженні CPU

Корінь: регресія оптимізатора або зміна поведінки статистик.

Фікс: захопіть EXPLAIN ANALYZE старого і нового; додайте/підкоригуйте індекси; оновіть гістограми де використано; розгляньте техніки стабільності плану (але підказки — технічний борг).

3) Симптом: репліки суттєво відстають тільки після оновлення

Корінь: оновлена репліка застосовує row‑події повільніше через іншу поведінку fsync, різні налаштування паралельної реплікації або обмеження IO.

Фікс: перевірте затримку IO, збільште паралелізм реплік де підтримано, переконайтесь, що binlog_format і налаштування row image узгоджені, і виправте насичення сховища перед cutover.

4) Симптом: перемикання триває вічно, підключення накопичуються

Корінь: довгі транзакції / метадані‑блокування / повільне відновлення після збою на кандидаті в primary.

Фікс: виявляйте і зупиняйте довгі транзакції перед cutover; дотримуйтесь правил вікна техобслуговування для пакетних джобів; перевірте час відновлення після збою під контролем у репетиції.

5) Симптом: дані виглядають «не такими» (порядок, унікальність, порівняння)

Корінь: відмінності колацій/набору символів або зміни в поведінці порівняння JSON/text між рушіями.

Фікс: явно зафіксуйте character_set_server/collation_server; перевірте стовпці зі змішаними колаціями; додавайте COLLATE в критичних запитах за потреби; уникайте зміни колацій під час оновлення.

6) Симптом: дашборди моніторингу зламались у ніч оновлення

Корінь: змінені статусні змінні, налаштування Performance Schema або зміни привілеїв для користувача моніторингу.

Фікс: протестуйте агента моніторингу проти оновленої репліки під час репетиції; оновіть запити і гранти; тримайте мінімальну «DB heartbeat» перевірку незалежну від складних дашбордів.

7) Симптом: диск заповнюється під час оновлення

Корінь: ріст binlog, spill тимчасових таблиць, розширення redo log під час догону або залишки артефактів оновлення.

Фікс: забезпечте запас місця; моніторте /var/lib/mysql і tmpdir; встановіть tmpdir на том, розрахований на найгірші сорти; не скорочуйте агресивно термін зберігання binlog під час міграції.

8) Симптом: запити, які раніше «працювали», тепер дають помилки

Корінь: більш строгий sql_mode, зарезервовані слова або змінені значення за замовчуванням.

Фікс: інвентаризуйте sql_mode і зафіксуйте його; запустіть тестовий набір застосунку; шукайте в логах «deprecated» і «error near»; виправляйте запити, а не послаблюйте правильність глобально, якщо не потрібно.

FAQ

1) Чи варто оновлювати MySQL 5.7 до 8.0 ін‑плейс?

Тільки якщо простій прийнятний і у вас є протестований шлях відновлення. Для всього важливого спочатку оновлюйте репліки, потім виконуйте перемикання.

2) Чи можу я поміняти MariaDB на MySQL просто замінивши бінарники?

Ні. Розглядайте це як міграцію. Словники даних, системні таблиці, GTID і поведінка фіч розходяться. Побудуйте новий кластер і перемістіть дані з валідацією.

3) Яка найнадійніша стратегія відкату?

Збережіть старий primary вцілілим і записуваним тільки якщо ви виконуєте контрольований failback з відомим напрямом реплікації. Інакше тримайте його у режимі тільки для читання і покладайтесь на відновлення/CDC. Відкат має бути відрепетированим.

4) Як виявити регресії оптимізатора до cutover?

Виберіть 20–50 критичних запитів, захопіть EXPLAIN ANALYZE і статистику виконання на старій версії, потім порівняйте на оновленій репліці з продакшен‑подібними даними і навантаженням.

5) Мій застосунок використовує старий конектор. Що ламається першим у MySQL 8?

Аутентифікація і TLS — типові точки відмови: caching_sha2_password, більш строгі очікування шифрів і підтримка проксі. Оновіть клієнти/проксі до сервера, якщо можете.

6) Чи потрібно запускати mysql_upgrade?

Залежить від версії і дистрибуції. Сучасні версії MySQL інтегрують багато логіки оновлення, але вам все одно треба слідувати рекомендованим пост‑крокам від вендора. Операційне правило: перевірте системні таблиці і виконайте рекомендовану процедуру оновлення спочатку на репліці.

7) А що з Galera — чи можна робити поетапні оновлення?

Іноді. Залежить від точних версій MariaDB/Galera. Перевірте підтримуваний шлях і протестуйте в репетиції. Якщо поетапне оновлення не підтримується — побудуйте новий кластер і зробіть cutover.

8) Чи варто змінювати значення конфігу під час оновлення?

Не робіть цього, якщо немає конкретної причини й підтвердження в тестах. Зміна версії рушія вже велика змінна. Розділяйте налаштування продуктивності в окремий проєкт, якщо не хочете неоднозначних кореневих причин.

9) Скільки часу тримати старий кластер після cutover?

Достатньо, щоб покрити реалістичне «вікно виявлення помилок» — часто дні, а не години — зважено з витратами і ризиками. Тримайте його у стані, що підтримує ваш план відкату (тільки для читання або готовим до failback).

10) Який найшвидший спосіб отримати впевненість одразу після cutover?

Слідкуйте за трьома речами: рівень помилок від застосунку, p95/p99 затримки кількох ключових ендпоїнтів та здоров’я реплікації (якщо є downstream репліки). Якщо щось відхиляється — призупиніть і швидко прийміть рішення.

Висновок: практичні наступні кроки

Якщо ви нічого більше не візьмете: оновлення — це не хобі на вихідні. Це контрольована зміна найстанішої системи, яку ви експлуатуєте.

  1. Виберіть метод: поетапне оновлення через реплікацію для оновлень одного рушія; міграція для переходів MySQL ↔ MariaDB.
  2. Репетируйте: відновіть реальні дані у стенді, запустіть навантаження, порівняйте плани, протестуйте моніторинг.
  3. Зробіть відкат реальним: визначте тригери, тримайте старий primary/кластер у стані, готовому для відкату, і практикуйте процедуру.
  4. Оновлюйте репліки спочатку: валідуйте годинами під навантаженням, а не хвилинами.
  5. Переключайте дисципліновано: призупиніть критичні пакетні джоби, спостерігайте помилки/затримки/реплікацію і будьте готові відмінити операцію.

Мета не в успішній команді оновлення. Мета — продакшен, який працює передбачувано наступного ранку.

← Попередня
Контейнери Docker з кількома мережами: припиніть випадкове відкриття доступу до невірної мережі
Наступна →
Продуктивність Ceph на Proxmox повільна: 10 перевірок, які справді знаходять вузьке місце

Залишити коментар