Оновлення MySQL (Percona Server) з 5.7 до 8.0

Оновлення MySQL (Percona Server) з 5.7 до 8.0

Прогрес не стоїть на місці, тому причини оновитися на актуальні версії MySQL стають все більш вагомими. Нещодавно в одному з наших проектів настав час оновлювати затишні кластери Percona Server 5.7 до 8-ї версії. Все це відбувалося на платформі Ubuntu Linux 16.04. Як виконати подібну операцію з мінімальним простоєм та з якими проблемами ми зіткнулися під час оновлення – читайте у цій статті.

Підготовка

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

Перед оновленням ми обов'язково звернемося до офіційної документації:

І складемо план дій:

  1. Виправити конфігураційні файли, видаляючи застарілі директиви.
  2. Перевірити сумісність утиліт.
  3. Оновити slave-бази, поставивши пакет percona-server-server.
  4. Обновити майстер, поставивши той самий пакет.

Розберемо кожен пункт плану і подивимося, що може піти негаразд.

ВАЖЛИВО! Процедура оновлення MySQL кластера на базі Galera має свої тонкощі, які в статті не описані. Не варто використовувати цю інструкцію у такому випадку.

Частина 1: Перевірка конфігів

У 8-й версії MySQL прибрали query_cache. Взагалі він був визнаний застарілим ще у версії 5.7, але тепер і видалений зовсім. Відповідно, необхідно прибрати пов'язані директиви. А для кешування запитів тепер можна використовувати зовнішні інструменти, наприклад, ProxySQL.

Також у конфізі знайшлися застарілі директиви про innodb_file_format. Якщо MySQL 5.7 була можливість вибору формату InnoDB, то 8-я версія вже працює тільки з форматом Barracuda.

Наш підсумок – видалення наступних директив:

  • query_cache_type, query_cache_limit и query_cache_size;
  • innodb_file_format и innodb_file_format_max.

Для перевірки скористаємося Docker-образом Percona Server. Конфіг сервера помістимо до директорії mysql_config_test, а поруч створимо директорії для даних та логів. Приклад тесту конфігурації percona-server:

mkdir -p {mysql_config_test,mysql_data,mysql_logs}
cp -r /etc/mysql/conf.d/* mysql_config_test/
docker run  --name some-percona -v $(pwd)/mysql_config_test:/etc/my.cnf.d/  -v $(pwd)/mysql_data/:/var/lib/mysql/ -v $(pwd)/mysql_logs/:/var/log/mysql/ -e MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD} -d percona:8-centos

Підсумок: або в логах Docker, або в директорії з логами, залежно від ваших конфігів, з'явиться файл, в якому будуть описані проблемні директиви.

Ось що було в нас:

2020-04-03T12:44:19.670831Z 0 [Warning] [MY-011068] [Server] The syntax 'expire-logs-days' is deprecated and will be removed in a future release. Please use binlog_expire_logs_seconds instead.
2020-04-03T12:44:19.671678Z 0 [Warning] [MY-013242] [Server] --character-set-server: 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous.
2020-04-03T12:44:19.671682Z 0 [Warning] [MY-013244] [Server] --collation-server: 'utf8_general_ci' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead.

Таким чином, нам потрібно ще розібратися з кодуванням і замінити застарілу директиву. expire-logs-days.

Частина 2: Перевірка працюючих установок

У документації по оновленню є 2 утиліти для перевірки бази на сумісність. Їхнє використання допомагає адміністратору перевірити сумісність наявної структури даних.

Почнемо з класичної утиліти mysqlcheck. Достатньо просто запустити:

mysqlcheck -u root -p --all-databases --check-upgrade

Якщо проблеми не виявлено, утиліта завершиться з кодом 0:

Оновлення MySQL (Percona Server) з 5.7 до 8.0

Крім того, у сучасних версіях MySQL доступна утиліта mysql-shell (у випадку Percona це пакет percona-mysql-shell). Вона є заміною класичному клієнту mysql і поєднує функції клієнта, редактора SQL-коду та інструменти адміністрування MySQL. Для перевірки сервера перед оновленням можна через неї виконати такі команди:

mysqlsh -- util check-for-server-upgrade { --user=root --host=1.1.1.1 --port=3306 } --config-path=/etc/mysql/my.cnf

І ось які зауваження ми отримали:

Оновлення MySQL (Percona Server) з 5.7 до 8.0

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

Оновлення MySQL (Percona Server) з 5.7 до 8.0

Ми вирішили, що оновлення має бути без проблем.

Зауваження про попередження вище, що свідчать про проблеми з кодуванням. Справа в тому, що UTF-8 у MySQL до недавнього часу не була «справжньою» UTF-8, тому що зберігала всього 3 байти замість 4. У MySQL 8 це нарешті вирішили виправити: аліас utf8 невдовзі вестиме на кодування utf8mb4, а старі стовпці у таблицях стануть utf8mb3. Надалі кодування utf8mb3 буде видалено, але не в даному релізі. Тому ми вирішили виправити кодування вже на інсталяції СУБД, що працює, після її оновлення.

Частина 3: Оновлення серверів

Що ж може піти не так, коли є такий розкішний план?.. Чудово розуміючи, що нюанси завжди трапляються, перший експеримент ми провели на dev-кластері MySQL.

Як уже згадувалося, офіційна документація висвітлює питання оновлення MySQL-серверів із репліками. Суть зводиться до того, що спочатку варто оновлювати всі репліки (slave), оскільки MySQL 8 може реплікуватися з майстра версії 5.7. Певна складність полягає в тому, що у нас використовується режим master <-> master, коли віддалений майстер перебуває в режимі тільки для читання. Тобто фактично бойовий трафік надходить в один ЦОД, а другий є резервним.

Топологія виглядає так:

Оновлення MySQL (Percona Server) з 5.7 до 8.0

Оновлення має розпочатися з реплік mysql replica dc 2, mysql master dc 2 и mysql replica dc 1, а закінчитися - сервером mysql master dc 1. Для більшої надійності ми зупинили віртуальні машини, зробили їх снапшоти, а безпосередньо перед оновленням зупинили реплікацію командою STOP SLAVE. В іншому ж оновлення виглядає так:

  1. Кожну репліку перезапускаємо, додавши до конфіг 3 опції: skip-networking, skip-slave-start, skip-log-bin. Справа в тому, що оновлення бази генерує бінарні логи із оновленням системних таблиць. Дані директиви гарантують, що в базі не буде зміни даних програми, а до бінарних логів не потрапить інформація про оновлення системних таблиць. Це дозволить уникнути проблем під час відновлення реплікації.
  2. Встановлюємо пакет percona-server-server. Важливо відзначити, що у версії MySQL 8 НЕ потрібно запускати команду mysqlupgrade після оновлення сервера.
  3. Після успішного старту ще раз перезапускаємо сервер уже без параметрів, які додавалися в першому пункті.
  4. Переконуємось, що реплікація успішно працює: перевіряємо SHOW SLAVE STATUS і бачимо, що оновлюються таблиці з лічильниками на основі докладання.

Все це досить просто: оновлення dev пройшло успішно. Ок, можна спокійно планувати нічне оновлення для виробництва.

Не було смутку — prod ми оновлювали

Однак перенесення успішного досвіду dev на production не обійшлося без сюрпризів.

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

2020-01-14T21:43:21.500563Z 2 [ERROR] [MY-012069] [InnoDB] table: t1 has 19 columns but InnoDB dictionary has 20 columns
2020-01-14T21:43:21.500722Z 2 [ERROR] [MY-010767] [Server] Error in fixing SE data for db1.t1
2020-01-14T21:43:24.208365Z 0 [ERROR] [MY-010022] [Server] Failed to Populate DD tables.
2020-01-14T21:43:24.208658Z 0 [ERROR] [MY-010119] [Server] Aborting

Дослідження архівів різних поштових розсилок у Google призвело до розуміння, що така проблема виникає через бага MySQL. Хоча скоріше це навіть баг утиліт mysqlcheck и mysqlsh.

Виявляється, MySQL змінили спосіб представлення даних для десяткових полів (int, tinyint і т.п.), тому всередині mysql-server використовується інший спосіб їх зберігання. Якщо ваша база даних спочатку була у версії 5.5 або 5.1, а потім ви оновлювалися до 5.7, то, можливо, потрібно зробити OPTIMIZE для деяких таблиць. MySQL оновить файли з даними, перевівши їх на актуальний формат зберігання.

Також це можна перевірити утилітою mysqlfrm:

mysqlfrm --diagnostic -vv /var/lib/mysql/db/table.frm
...
 'field_length': 8,
  'field_type': 246, # формат поля
  'field_type_name': 'decimal',
  'flags': 3,
  'flags_extra': 67,
  'interval_nr': 0,
 'name': 'you_deciaml_column',
...

Якщо field_type у вас дорівнює 0, то в таблиці використовується старий тип - треба проводити OPTIMIZE. Однак, якщо стоїть значення 246, у вас вже новий тип. Детальніше з типами можна ознайомитись у коді.

Більше того, в даному базі розглядається друга можлива причина, яка обійшла нас стороною, - це відсутність InnoDB-таблиць у системній таблиці INNODB_SYS_TABLESPACESякщо вони, таблиці, створювалися у версії 5.1. Щоб уникнути проблем при оновленні, можна скористатися доданим SQL-скриптом.

Чому ж у нас не виникло таких проблем на dev? База туди періодично копіюється з production - таким чином, таблиці перетворюються.

На жаль, на реально працюючій великій БД не вийде просто взяти і виконати повсюдний OPTIMIZE. Тут допоможе percona-toolkit: для операції online OPTIMIZE відмінно підходить утиліта pt-online-schema-change.

Оновлений план став таким:

  1. Провести оптимізацію всіх таблиць.
  2. Провести оновлення бази даних.

Щоб перевірити його і заразом з'ясувати час оновлення, ми відключили одну з реплік, а для всіх таблиць запустили наступну команду:

pt-online-schema-change --critical-load Threads_running=150 --alter "ENGINE=InnoDB" --execute --chunk-size 100 --quiet --alter-foreign-keys-method auto h=127.0.0.1,u=root,p=${MYSQL_PASSWORD},D=db1,t=t1

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

Щоб цього уникнути, на production ми додали до команди аргумент --sleep зі значенням 10 – цей параметр регулює довжину очікування після перенесення пачки даних у нову таблицю. Так можна знизити навантаження, якщо реально запущений додаток вимогливий до часу відповіді.

Після виконання оптимізації оновлення пройшло успішно.

…але не до кінця!

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

Оновлення MySQL (Percona Server) з 5.7 до 8.0

На скріншоті видно пилкоподібний графік, пов'язаний з тим, що частина потоків сервера MySQL періодично падали з помилкою. У програмі з'явилися помилки:

[PDOException] SQLSTATE[HY000] [2002] Connection refused

Побіжний огляд логів виявив, що демон mysqld було отримати необхідні ресурси в операційної системи. Розбираючись із помилками, ми виявили в системі «безхазяйні» файли політик apparmor:

# dpkg -S /etc/apparmor.d/cache/usr.sbin.mysqld
dpkg-query: no path found matching pattern /etc/apparmor.d/cache/usr.sbin.mysqld
# dpkg -S /etc/apparmor.d/local/usr.sbin.mysqld
dpkg-query: no path found matching pattern /etc/apparmor.d/local/usr.sbin.mysqld
# dpkg -S /etc/apparmor.d/usr.sbin.mysqld
mysql-server-5.7: /etc/apparmor.d/usr.sbin.mysqld
# dpkg -l mysql-server-5.7
rc  mysql-server-5.7 5.7.23-0ubuntu0.16.04.1      amd64

Ці файли утворилися при оновленні на MySQL 5.7 кілька років тому і належать віддаленому пакету. Видалення файлів і перезапуск служби Apparmor вирішили проблему:

systemctl stop apparmor
rm /etc/apparmor.d/cache/usr.sbin.mysqld
rm /etc/apparmor.d/local/usr.sbin.mysqld
rm /etc/apparmor.d/usr.sbin.mysqld
systemctl start apparmor

На закінчення

Будь-яка, навіть найпростіша операція може призвести до несподіваних проблем. І навіть наявність продуманого плану не завжди гарантує очікуваний результат. Тепер у будь-які плани оновлення наша команда входить ще й обов'язкове чищення зайвих файлів, які могли з'явитися в результаті останніх дій.

А цією не дуже професійною графічною творчістю я хотів би сказати величезне спасибі компанії Percona за їх відмінні продукти!

Оновлення MySQL (Percona Server) з 5.7 до 8.0

PS

Читайте також у нашому блозі:

Джерело: habr.com

Додати коментар або відгук