RE:NODE

Обновление MySQL 8.0 до 8.4: проверка, бэкап, апгрейд, откат

Автор Команда RE:NODE
16 мин чтения


Обновление MySQL 8.0 до 8.4 состоит из четырёх частей, и порядок важнее любой отдельной команды. Запустите проверку обновления из MySQL Shell на работающем сервере 8.0 и исправьте все ошибки, которые она покажет. Сделайте бэкап, который можно восстановить в 8.0: холодную копию каталога данных для in-place обновления и логический дамп для всего остального. Затем либо запустите бинарники 8.4 на старом каталоге данных (in-place), либо загрузите дамп в чистый сервер 8.4 (logical). Наконец, протестируйте приложение на 8.4 до того, как придёт настоящий трафик: обратного пути in-place нет, и если 8.4 однажды запустился на каталоге данных, 8.0 откажется его открывать.

Что меняется между версиями - плагин аутентификации по умолчанию, удалённые команды, новые настройки InnoDB - описано в статье MySQL 8.4 LTS: что изменилось. Это руководство - про процедуру: команды, смысл вывода проверки, оба маршрута обновления подробно, тестирование и то, как далеко на самом деле заходит откат.

In-place или logical: сначала выберите маршрут#

Оба маршрута заканчиваются тем, что ваши данные оказываются на 8.4. Они различаются временем простоя, требуемым уровнем доступа и тем, как выглядит откат.

In-placeLogical (dump and load)
Что происходитБинарники 8.4 запускаются на каталоге данных 8.0Данные экспортируются в SQL или файлы и загружаются в новый сервер 8.4
Что нужноRoot на машине, где работает MySQLУчётная запись на обоих серверах
ПростойМинуты плюс обновление словаря данныхВремя дампа плюс время загрузки, обычно гораздо дольше
Скорость на больших данныхБыстро - данные не переписываютсяМедленно - каждая строка и индекс строятся заново
Старый сервер потомИсчезает, если вы не скопировали файлыНе тронут, продолжает работать на 8.0
БонусНетТаблицы пересобираются, фрагментация и старые форматы строк уходят

Выбирайте in-place, если вы сами администрируете MySQL на VDS или своей машине, а данных так много, что перезагрузка заняла бы часы. Выбирайте logical, если база живёт в облачном сервисе, где сервером управляет кто-то другой, если вы заодно переезжаете на новую машину или если данных немного - несколько гигабайт - и загрузка займёт минуты. У logical-обновления к тому же самый удобный откат: сервер 8.0 остаётся на месте и не меняется, пока вы его не выключите.

Есть и третий маршрут для тех, кто не может позволить себе простой: сделайте новый сервер 8.4 репликой источника 8.0, дайте ему догнать, затем переключите на него приложение. Репликация со старого источника на более новую реплику поддерживается, обратное - нет, поэтому как только записи пошли на 8.4, старый сервер 8.0 уже не сможет за ним следовать. Для небольшого приложения такой маршрут избыточен, и подробно он здесь не рассматривается.

Проверкана живом 8.0Исправить ошибкиконфиг, аккаунты, схемаBackupвосстановлен хотя бы разIn-place8.4 на старом datadirDump and loadв новый 8.4Тест приложениядо реального трафика
Порядок обновления с 8.0 до 8.4

Шаг 1: сначала бэкап, в формате, который читает 8.0#

У бэкапа перед обновлением есть задача сверх обычной: его должно быть можно восстановить в 8.0, потому что именно к этой версии вы будете возвращаться. Подходят два вида.

Холодная копия каталога данных. Корректно остановите 8.0 и скопируйте весь каталог - обычно это /var/lib/mysql на Linux - в другое место. Это единственный бэкап, который быстро отменяет in-place обновление: возвращаете копию на место, ставите 8.0, запускаете - и вы снова там, где были. Ни для чего другого он не годится, потому что открывается только на той же мажорной версии, но для этой цели он идеален. Снимок файловой системы (LVM, ZFS) корректно остановленного сервера выполняет ту же роль и делается за секунды.

bash
$ sudo systemctl stop mysql          # mysqld on RHEL-family systems$ sudo cp -a /var/lib/mysql /var/backups/mysql-80-datadir$ sudo cp -a /etc/mysql /var/backups/mysql-80-etc

Скопируйте и конфигурацию. Обновление заставит вас её править, а для отката нужна версия под 8.0.

Логический дамп. Это бэкап для logical-маршрута и вторая линия защиты для in-place. mysqldump с флагами --single-transaction --routines --events --triggers создаёт согласованный SQL-файл без остановки сервера; mysqldump: бэкап и восстановление объясняет каждый флаг и ошибки, которые при этом возникают. Для баз побольше быстрее утилита дампа из MySQL Shell, потому что она пишет параллельно:

javascript
// in mysqlsh, JavaScript mode (\js)util.dumpInstance("/var/backups/dump-80", {threads: 4})

util.dumpInstance() по умолчанию включает пользователей и права, с чем mysqldump справляется плохо: он выгружает схему mysql как обычные таблицы, а загрузка их в другую версию - это напрашивание на неприятности. Если вы используете mysqldump, сохраните аккаунты отдельно: для каждой учётной записи приложения SHOW CREATE USER и SHOW GRANTS дают выражения, которые можно выполнить заново.

Шаг 2: запустите util.checkForServerUpgrade#

Проверка обновления находится в MySQL Shell (mysqlsh), а не в сервере. Она подключается к работающему серверу 8.0, изучает конфигурацию, аккаунты, схемы и хранимый код и сообщает, что сломается или поведёт себя иначе на целевой версии. Ничего она не меняет.

Нужен MySQL Shell не старше целевой версии: проверка умеет тестировать любую версию от 8.0.11 до собственного номера версии, и не дальше. Для цели 8.4 подойдёт и Shell 8.4, и любой релиз 9.x. Запускать её можно на любой машине, которая достаёт до сервера, в том числе на ноутбуке, но проверка файла конфигурации требует, чтобы файл лежал на той машине, где работает Shell, поэтому запуск на самом сервере базы даёт самый полный отчёт.

bash
$ mysqlsh -- util check-for-server-upgrade root@localhost:3306 \    --target-version=8.4.6 --config-path=/etc/mysql/my.cnf

То же самое из интерактивной сессии Shell в режиме JavaScript:

javascript
util.checkForServerUpgrade("root@localhost:3306", {  targetVersion: "8.4.6",  configPath: "/etc/mysql/my.cnf"})

Если опустить targetVersion, проверка возьмёт версию, соответствующую самому Shell, - то есть Shell 9.x без этого параметра проверяет на 9.x и сообщает о проблемах, которых у вас пока нет. Укажите ту патч-версию 8.4, которую действительно ставите. Если опустить configPath, проверки, читающие файл настроек, завершатся сообщением с просьбой его указать; в Debian и Ubuntu это файл /etc/mysql/my.cnf (он подключает mysql.conf.d/), в семействе RHEL - /etc/my.cnf.

Учётной записи, под которой запускается проверка, нужны привилегии PROCESS и SELECT. На собственном сервере проще всего взять root. На облачном сервере, где у вас есть только пользователь приложения, выдайте что можете и всё равно запустите: проверки, которым не хватает прав, так и сообщат об этом, а не пройдут молча.

Как читать отчёт

Текстовый вывод - это нумерованный список проверок, каждая из которых заканчивается строкой No issues found или списком находок с пометками Error, Warning или Notice, после чего идёт итог:

code
Errors:   2Warnings: 5Notices:  02 errors were found. Please correct these issues before upgradingto avoid compatibility issues.

Ошибки (Errors) блокируют обновление или не дают серверу запуститься - обычно это удалённая опция в файле конфигурации. Исправьте все на сервере 8.0 и запускайте проверку заново, пока их число не станет нулём. Предупреждения (Warnings) обновлению не мешают, но описывают то, что будет вести себя иначе или сломается позже; прочитайте каждое. Замечания (Notices) носят информационный характер.

Проверки, которые чаще всего срабатывают на сервере 8.0 при переходе на 8.4:

ПроверкаЧто находит
sysvarУдалённые опции в файле конфигурации и настройки, у которых меняется значение по умолчанию
authMethodUsageАккаунты, всё ещё использующие mysql_native_password
deprecatedDefaultAuthВсё ещё заданный default_authentication_plugin
pluginUsageПлагины, которые удалены или заменены компонентами
foreignKeyReferencesВнешние ключи, ссылающиеся на неуникальные или частичные ключи
reservedKeywordsИдентификаторы, ставшие зарезервированными словами
invalidPrivilegesПривилегии, которых больше нет или которые изменились

Команда mysqlsh -- util check-for-server-upgrade --list --target-version=8.4.6 выводит все проверки с описанием и версиями, к которым они применяются. Для автоматизации --output-format=JSON выдаёт один объект с полями errorCount, warningCount и массивом checksPerformed; шаг в CI или скрипт могут отказываться продолжать, пока errorCount больше нуля. Проверка не сигнализирует о проблеме кодом возврата, поэтому смотрите на счётчик, а не рассчитывайте, что команда завершится ошибкой.

Две находки заслуживают пояснения, как их исправлять. Для authMethodUsage переведите каждый аккаунт на caching_sha2_password командой ALTER USER ... IDENTIFIED WITH caching_sha2_password BY '...', предварительно убедившись, что драйвер приложения его поддерживает. Для sysvar отредактируйте файл настроек и удалите или переименуйте удалённые параметры; чаще всего это expire_logs_days (замените на binlog_expire_logs_seconds) и default_authentication_plugin. Оба исправления работают на 8.0, поэтому делайте их там, перезапустите и убедитесь, что приложение работает, до самого обновления. Так вы отделите «поломку вызвало изменение конфига» от «поломку вызвала 8.4», а это различие вам обязательно понадобится.

Шаг 3a: in-place обновление на Linux#

Когда проверка чистая, а холодная копия снята, in-place обновление короткое. Документация MySQL рекомендует перед in-place обновлением между сериями медленное завершение работы, чтобы InnoDB закончил purge и слияния change buffer до того, как файлы перейдут новой версии:

sql
SET GLOBAL innodb_fast_shutdown = 0;

Затем остановите сервер и замените пакеты. В Debian или Ubuntu с APT-репозиторием Oracle серия выбирается пакетом mysql-apt-config:

bash
$ sudo systemctl stop mysql$ sudo dpkg-reconfigure mysql-apt-config   # choose the 8.4 LTS series$ sudo apt update$ sudo apt install mysql-server

В RHEL, Rocky или Alma с yum-репозиторием Oracle отключите репозиторий 8.0 и включите репозиторий 8.4 LTS - точные имена покажет dnf repolist all | grep mysql в вашем пакете релиза, - а затем выполните sudo dnf upgrade mysql-community-server. MySQL из пакетов дистрибутива следует собственному графику выпуска и может вообще не предлагать 8.4; если apt policy mysql-server показывает только кандидатов 8.0, вам нужен репозиторий Oracle или контейнер.

В Docker замена - это смена тега. Остановите контейнер, замените image: mysql:8.0 на image: mysql:8.4 и запустите на том же томе. Фиксируйте 8.4 или конкретный патч, но не latest: он ушёл дальше 8.4, к Innovation-релизам.

Что происходит при первом запуске

Когда 8.4 стартует на каталоге данных 8.0, он обновляет словарь данных, затем системную схему mysql, схему sys и справочные таблицы. Следите за журналом ошибок, пока это происходит:

bash
$ sudo tail -f /var/log/mysql/error.log

Вы ищете пару строк Server upgrade from '80xxx' to '804xx' started и ... completed, а затем ready for connections. На сервере в несколько сотен таблиц это занимает секунды, на десятках тысяч - может занять минуты. Не прерывайте процесс на полпути: прерванное обновление словаря данных - как раз тот случай, ради которого существует холодная копия.

Поведением управляет серверная опция --upgrade, по умолчанию AUTO (обновлять всё, что требует обновления). --upgrade=FORCE повторно запускает обновление системных таблиц, даже если сервер считает, что оно уже выполнено, - это способ исправить ситуацию, когда есть подозрение, что системная схема обновлена наполовину. mysql_upgrade больше не существует, вручную запускать нечего.

Если сервер не стартует, причина названа в первых строках ошибок журнала. Обычно это опция, о которой предупреждала проверка и которую никто не удалил: в журнале появится unknown variable с именем опции. Удалите её из конфигурации и запустите снова.

Шаг 3b: logical обновление#

Для logical-маршрута нужен работающий сервер 8.4 - новая машина, новый контейнер или облачная база MySQL 8.4 - и дамп из шага 1. С выводом mysqldump это обычный импорт:

bash
$ gunzip -c appdb-80.sql.gz | mysql -h new-host -P 3306 -u root -p appdb

Для дампа из MySQL Shell используйте соответствующий загрузчик, который загружает таблицы параллельно:

javascript
util.loadDump("/var/backups/dump-80", {threads: 4, loadUsers: true})

util.loadDump() подводят две вещи. Он загружает данные через LOAD DATA LOCAL INFILE, поэтому на целевом сервере параметр local_infile должен быть равен ON; если он выключен и изменить его нельзя, используйте mysqldump. А loadUsers по умолчанию выключен, так что аккаунты и права пропускаются, пока вы их не запросите. На облачном сервере, где вы не полноценный администратор, часто чище вообще не загружать пользователей и создать учётные записи приложения самостоятельно - у вашей учётной записи может не быть права создавать аккаунты с произвольными привилегиями.

Смысл logical-маршрута - в репетиции. Загрузите данные в новый сервер, пока старый продолжает обслуживать трафик, проверьте всё на копии, а когда проверка пройдена, снимите свежий дамп на время короткой заморозки записи и загрузите его. Заморозка нужна только из-за записей между репетиционным и финальным дампом. Само переключение разбирает статья Перенос MySQL на новый хост: заморозка, финальный дамп, смена строк подключения, старый сервер в режиме только для чтения ещё несколько дней.

Definer - классический источник сбоев при импорте. Представление, триггер или процедура, созданные пользователем admin@localhost на старом сервере, несут DEFINER=admin@localhost, а на 8.4 для создания объекта с другим аккаунтом в роли definer нужна привилегия SET_ANY_DEFINER. Если импорт останавливается с ошибкой отказа в доступе на CREATE VIEW или CREATE TRIGGER, либо сначала создайте аккаунт definer, либо удалите эти конструкции из дампа.

Шаг 4: протестируйте приложение до прихода трафика#

Проверка обновления изучает базу данных. Приложения она не видит, а сюрпризы 8.4 проявляются именно в приложении. Тестируйте на обновлённой копии: репетиционный сервер для logical-маршрута, тестовый сервер или копия, восстановленная из холодного бэкапа, для in-place.

Что тестировать, примерно в порядке частоты поломок:

  1. Каждый аккаунт может войти. Не только основной пользователь приложения, но и аккаунт для отчётов только на чтение, пользователь бэкапа и тот, под которым работает cron-задача. Аккаунт, оставшийся на mysql_native_password, споткнётся именно здесь и больше нигде.
  2. Каждый клиент и драйвер подключается. Старые коннекторы - обычная причина сбоев: драйвер, не умеющий caching_sha2_password, сообщает, что плагин не может быть загружен. Проверьте драйверы языков, GUI-инструменты и любых агентов бэкапа и мониторинга. Поведение этого плагина с TLS описано в статье Удалённые подключения к MySQL.
  3. Набор тестов проходит. Запустите собственные тесты приложения на 8.4. Особое внимание - миграциям: ORM, создающий внешний ключ на неуникальном столбце, на 8.4 завершится ошибкой, хотя уже существующие ключи обновление пережили.
  4. Задания по расписанию и скрипты работают. Всё, что выполняет SHOW SLAVE STATUS, FLUSH HOSTS или RESET MASTER, теперь падает с синтаксической ошибкой. Поищите их в репозиториях и crontab.
  5. Медленные запросы остаются быстрыми. Значение sql_mode по умолчанию не изменилось по сравнению с 8.0, поэтому результаты запросов должны совпадать, но планы могут сместиться из-за новых настроек InnoDB и изменений в оптимизаторе. Возьмите самые частые запросы из performance_schema.events_statements_summary_by_digest на 8.0, выполните для каждого EXPLAIN на обеих версиях и сравните. Как читать различия, объясняет статья Индексы MySQL и EXPLAIN, а журнал медленных запросов поймает то, что вы пропустили, когда пойдёт реальный трафик.

В первый день следите за памятью на небольшом сервере. Значение temptable_max_ram по умолчанию в 8.4 - 3% памяти, но не меньше 1 GB, а на машине с 1 или 2 GB это позволяет временным таблицам занимать гораздо больше, чем задумано. Если вы не задавали его на 8.0, задайте явно сейчас.

Откат: что можно отменить, а что нельзя#

Об этом обычно узнают на собственном горьком опыте, поэтому важно сказать точно.

In-place понижение с 8.4 до 8.0 не поддерживается. Как только 8.4 запустился на каталоге данных, словарь данных несёт новую версию. Запустите на нём 8.0 - и InnoDB откажется с сообщением Downgrade is not supported. Инструмента для обратного преобразования нет. Единственный быстрый путь назад - холодная копия из шага 1: остановите 8.4, отложите в сторону его каталог данных, восстановите копию, переустановите пакеты 8.0 и старую конфигурацию, запустите. Всё, что было записано после снятия копии, теряется, поэтому окно между снятием копии и решением оставить 8.4 должно быть коротким.

Логический дамп из 8.4 в 8.0 поддерживается, но только для отката. Руководство по 8.4 называет dump and load (или репликацию) способом вернуться с 8.4 на 8.0 при условии, что в данных не использовалась новая функциональность 8.4. Так вы сохраните записи, сделанные после обновления: выгрузите сервер 8.4 и загрузите в 8.0. На больших данных это медленно и работает только пока схема не использует то, чего нет в 8.0, - новую привилегию в grant, GTID-тег, возможность, появившуюся в версиях с 8.1 по 8.4. Чем дольше вы работаете на 8.4, тем вероятнее, что что-то уже просочилось.

Logical-маршрут откатывается переключением строки подключения. Сервер 8.0 всё ещё работает. Если во время переключения вы держали его в режиме только для чтения, возврат мгновенный, за вычетом того, что за это время было записано на 8.4.

МаршрутПуть назадЗаписи после обновления
In-placeВосстановить холодную копию, переустановить 8.0Потеряны
In-placeДамп 8.4, загрузка в 8.0Сохранены, если не использовались возможности только 8.4
LogicalНаправить приложение обратно на 8.0Потеряны, если не выгружены

Определите точку невозврата заранее. Разумный вариант: храните холодную копию или старый сервер неделю и не используйте ничего специфичного для 8.4 в это время. После этого двигайтесь вперёд, а не назад: исправляйте проблемы на 8.4, ведь 8.0 больше не получает исправлений безопасности.

Устранение проблем при обновлении#

Большинство сбоев укладывается в несколько шаблонов, и журнал ошибок или сообщение клиента называет их.

Сервер не стартует после замены пакетов: прочитайте первые строки [ERROR] в журнале. unknown variable означает удалённую опцию в одном из файлов конфигурации - проверьте подключаемые каталоги, а не только основной файл. Сообщение о том, что каталог данных принадлежит более новой версии, значит, что вы запускаете 8.0 на обновлённом каталоге; восстановите холодную копию.

Приложение не может войти, а mysql может: аккаунт остался на mysql_native_password или драйвер слишком старый для caching_sha2_password. Временной мерой mysql_native_password=ON в секции [mysqld] снова включает старый плагин, но в 9.0 его уже нет, поэтому считайте это отсрочкой на несколько дней, а не решением.

Проверка ничего не нашла, а импорт падает: проверка изучает исходный сервер, а не ваш файл дампа. Ошибки definer, строки SET @@GLOBAL.GTID_PURGED и отказы из-за local_infile - это проблемы времени загрузки; в руководстве по mysqldump разобран каждый случай.

Запросы стали медленнее после in-place обновления: выполните ANALYZE TABLE для самых загруженных таблиц, чтобы статистика была свежей, затем сравните планы. Если какая-то нагрузка опиралась на адаптивный хеш-индекс, innodb_adaptive_hash_index=ON возвращает поведение 8.0; измеряйте до и после, а не включайте его по умолчанию.

Как это работает на RE:NODE#

Линейка MySQL на RE:NODE уже работает на MySQL 8.4 LTS, поэтому переезд туда с сервера 8.0 в другом месте - это logical-маршрут: запустите проверку на старом сервере, сделайте дамп и загрузите его в новый сервер с паролем root и базой приложения, которые для него сгенерированы. Бинарники сервера вы не запускаете, поэтому in-place обновления делать не нужно, а бэкапы панели - по требованию или по расписанию, с восстановлением по кнопке и хранением вне машины - защищают базу после переезда. Протестируйте новый сервер до переключения строки подключения приложения и держите старый сервер 8.0 нетронутым, пока не убедитесь, что всё в порядке.

FAQ#

Можно ли обновиться с MySQL 5.7 сразу до 8.4?

In-place - нет. Сначала нужно перейти с 5.7 на 8.0, затем с 8.0 на 8.4. Логический дамп из 5.7 обычно загружается в 8.4 напрямую, исключения - зарезервированные слова, нулевые даты в строгом режиме и старые плагины аутентификации, поэтому до начала запустите проверку на сервере 5.7 с целевой версией 8.4.

Меняет ли проверка обновления что-нибудь на моём сервере?

Нет. Она только читает: конфигурацию, метаданные из information_schema и схемы mysql, а также хранимый код. Она открывает собственную сессию и не трогает текущую сессию Shell. Её безопасно запускать на рабочем сервере в рабочее время.

Сколько длится in-place обновление?

Замена пакетов занимает минуту-две. Обновление словаря данных при первом запуске зависит от числа таблиц, а не от объёма данных: секунды для обычного приложения, минуты - для приложения с десятками тысяч таблиц. Медленное завершение работы заранее может добавить времени на загруженном сервере с большим объёмом ожидающего purge.

Переходить на 8.4 или сразу на релиз 9.x?

На 8.4 для рабочей базы. Это серия с долгосрочной поддержкой, исправления в ней выходят без изменений поведения. Innovation-релизы 9.x поддерживаются только до выхода следующего и продолжают удалять функциональность, в том числе mysql_native_password целиком.

Нужно ли обновлять драйвер MySQL в приложении?

Только если он не умеет caching_sha2_password, а это верно для старого пакета mysql для Node и для драйверов времён 5.7. Драйверы последних нескольких лет с ним справляются. Проверьте, подключившись к обновлённой копии именно той версией драйвера, которая поставляется с приложением.