Недавно мы обновили свои сервера с MySQL 5.7 на 8.0.
Оставим за рамками этой статьи зачем и какие новые плюшки появились в MySQL 8.0, а вместо этого расскажем о том, с какими сложностями мы столкнулись в процессе обновления.
Во-первых, перед обновлением стоит посмотреть на список изменений и поправить свой конфиг-файл.
Как минимум, удалены следующие параметры:
innodb_file_format, innodb_file_format_check, innodb_file_format_max,innodb_large_prefix
query_cache_limit, query_cache_min_res_unit, query_cache_size, query_cache_type, query_cache_wlock_invalidate.
В параметре sql_mode удалён в частности NO_AUTO_CREATE_USER — что особенно важно, т.к. в MySQL 5.7 он был включен по-умолчанию.
Инструкция по in-place upgrade есть у Percona. И в общем случае можно следовать ей, однако нам удалось так обновить только один кластер, у остальных попытка такого обновления заканчивалась неудачно с подобной ошибкой:
2019-06-22T05:04:18.510888Z 1 [ERROR] [MY-011014] [Server] Found partially upgraded DD. Aborting upgrade and deleting all DD tables. Start the upgrade process again.
2019-06-22T05:04:23.115018Z 0 [ERROR] [MY-010020] [Server] Data Dictionary initialization failed.
2019-06-22T05:04:23.115655Z 0 [ERROR] [MY-010119] [Server] Aborting
Поэтому остальные кластеры мы обновляли путем поднятия нового пустого инстанса и восстановления дампа БД с предыдущей версии.
Чтобы сделать такое, во-первых, потребуется дамп БД. И тут поджидает опасность #1 — сделанный стандартным образом дамп:
mysqldump -u root -p --hex-blob --default-character-set=utf8mb4 --all-databases --triggers --routines --events > dump.sql
не восстанавливается, выдавая ошибку:
ERROR 3554 (HY000) at line 15915: Access to system table 'mysql.innodb_index_stats' is rejected.
Описание есть в багтрекере MySQL (со статусом Not a bug :), там же есть и совет как делать дамп, чтобы его таки можно было восстановить:
mysqldump -u root -p --hex-blob --default-character-set=utf8mb4 --all-databases --triggers --routines --events --ignore-table=mysql.innodb_index_stats --ignore-table=mysql.innodb_table_stats > dump.sql
Но при попытке использовать такой дамп, если в нём присутствовали триггеры (а у нас они были), может поджидать опасность #2, в виде вот такой ошибки:
ERROR 1231 (42000) at line 54: Variable 'sql_mode' can't be set to the value of 'NO_AUTO_CREATE_USER'
Причина этого в том, как MySQL использует sql_mode для триггеров, а именно: MySQL сохраняет значение sql_mode для триггера в момент его создания и выполняет его потом всегда с этим значением. И соответственно сохраняет это значение в дамп.
Описание этого в справке:
dev.mysql.com/doc/refman/8.0/en/create-trigger.html
MySQL stores the sql_mode system variable setting in effect when a trigger is created, and always executes the trigger body with this setting in force, regardless of the current server SQL mode when the trigger begins executing.
Что же нам делать? Мы просто с помощью sed вырезали NO_AUTO_CREATE_USER из готового дампа. Подобной командой:
sed "s/50003 SET sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER/50003 SET sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO/g" dump.sql > dump2.sql
После этого дамп успешно восстанавливается, но нас поджидает опасность #3 (вполне ожидаемая правда) — системные таблицы восстановлены в состоянии от версии 5.7 и в логах у нас следующие ошибки:
[ERROR] [MY-013143] [Server] Column count of mysql.user is wrong. Expected 51, found 45. The table is probably corrupted
По опыту работы с прошлыми версиями это лечится запуском mysql_upgrade — но начиная с версии 8.0.16 — это не работает, т.к. mysql_upgrade объявлен deprecated и ничего не делает.
Теперь, чтобы вызвать обновление системных таблиц, необходимо запустить MySQL с опцией upgrade=FORCE.
На свежей ubuntu это можно сделать следующим образом:
systemctl set-environment MYSQLD_OPTS="--upgrade=FORCE"
После чего перезапустить MySQL. Ну и после успешного обновления удалить её:
systemctl unset-environment MYSQLD_OPTS