1. 问题背景:升级8.0.44后直接登录失败,到底发生了什么
先描述一下这个报错的实际场景。假设你的环境原本是MySQL 5.7或更早版本,通过某种方式(原地升级、逻辑迁移或冷备恢复)切换到8.0.44之后,执行mysql -uroot -p输入密码回车,结果直接弹出一行日志级别的错误,内容类似:
ERROR 1805 (HY000): Function 'mysql_native_password' is deprecated. The server is not running with the system tables using the MyISAM storage engine.实际上8.0.44的登录报错在终端里看起来更直接一些,MySQL服务可能仍然在运行,但任何账号都无法通过正常通道登录。有的朋友打算用mysqld --skip-grant-tables先绕过认证,结果进去之后发现mysql库里的表全都"坏了",想改密码、改权限、创建用户,全部报同样的MyISAM错误。
先说结论:这不是密码错误,也不是认证插件配置错误,而是系统表(mysql库下的表,比如user表、db表、tables_priv表等)的存储引擎结构,和8.0.44版本的服务端程序预期不一致。升级过程中,系统表没有被正确迁移到InnoDB,或者部分表仍然保留了MyISAM物理结构,导致8.0.44的代码在访问这些表时直接拒绝执行。
群里很多人遇到这个问题第一反应是"把data目录重新初始化",这确实能解决,但代价是你原来的用户、权限、所有库表数据可能全部丢失,而且数据目录重新初始化之后还需要手动重建所有账号和授权。对于一个生产环境来说,这是非常痛苦的一条路。这篇博文我会把问题拆开讲清楚,把背后的原理、安全修复步骤、以及如何避免下次升级再踩同样的坑,全部过一遍。
2. 为什么MySQL 8.0.44对系统表的存储引擎这么敏感
2.1 从MySQL 5.7升级而来的历史包袱
MySQL 5.7及之前的版本,系统表(mysql库)默认使用MyISAM存储引擎。用户表、权限表、日志表,几乎都是MyISAM。这个设计在早期版本里问题不大,因为MyISAM结构简单、读取快、占用内存少,但它的短板也很明显:不支持事务、不支持行级锁、崩溃后容易损坏。
到了MySQL 8.0,官方彻底改变了策略,把所有系统表统一改成InnoDB,并且在代码层面做了硬性检查——服务端启动时、访问授权表时,都会检查这些表的结构类型。如果检查到系统表还是MyISAM,就拒绝在这个表上执行任何操作,哪怕这个操作只是登录验证。
8.0.44这个版本更加严格,它甚至在MySQL Server启动阶段就检查mysql库下的表引擎元数据。如果你的data目录是从5.7直接拷贝过来的,或者升级过程中mysql_upgrade这一步没有正确执行,系统表就依然是MyISAM结构。这个时候服务端可能能启动(因为--daemonize启动的进程不一定立刻访问授权表),但当你尝试登录时,服务端读取mysql.user表,发现这个表不是InnoDB,直接抛出错误。
2.2 MySQL 8.0把插件和参数也绑定到了系统表结构上
这里还有一个容易被忽视的细节:MySQL 8.0默认的认证插件是caching_sha2_password,而5.7时代普遍用的是mysql_native_password。8.0.44版本里,官方对mysql_native_password做了弃用标记,但兼容模式仍然是可用的,前提是系统表结构正确升级到InnoDB。
我自己在测试环境里模拟过一次:把5.7.42的data目录整体拷贝到8.0.44的datadir路径下,然后启动8.0.44,服务端日志里会出现一条警告:
[Warning] [MY-010901] [Server] The 'mysql_native_password' authentication plugin is deprecated. Please consider using 'caching_sha2_password' instead.这条警告一般不会阻止登录,但如果mysql.user表还是MyISAM,8.0.44就会把登录流程卡死在验证阶段,错误信息里明确出现"system tables"和"MyISAM"这两个关键词。
另外,8.0.44里如果启用了mysql_native_password=ON这类参数,而又碰上了系统表不一致,错误信息还会额外提示Function 'mysql_native_password' is deprecated,导致很多人误以为是密码插件问题,结果去改default_authentication_plugin,改完依然登录失败,白白浪费时间。
2.3 正确理解"系统表不支持MyISAM存储引擎"这句话
很多运维朋友看到报错里的"不支持"三个字,第一反应是"MySQL 8.0.44不支持MyISAM了"。这话不完全对。
MySQL 8.0.44仍然支持MyISAM存储引擎,你完全可以在自己的业务库里建MyISAM表(虽然我不建议这么做),只是系统表(mysql库下的表)不允许再使用MyISAM。也就是说,这个限制是针对系统表这一类特殊的表,不是针对MyISAM这个引擎本身。
打个比方:一个公司规定"核心资产必须存放在保险柜里",结果你从旧办公室搬过来时,把核心资产放在普通储物柜里就搬进去了。保安(MySQL服务端)检查后发现储物柜不符合规定,于是拒绝让你使用保险柜里的任何东西。储物柜本身不是违禁品,你自家的东西放储物柜没问题,但核心资产(系统表)必须进保险柜(InnoDB)。
所以我后面给的解决方案里,有一条核心思路就是把"普通储物柜里的东西"全部搬进"保险柜"——也就是把mysql库下所有MyISAM表改成InnoDB。
3. 三种解决思路:从保守到彻底,你自己选
3.1 方案一:用skip-grant-tables临时进入,原地转换系统表引擎
这是最保守、数据最不折腾的方案,前提是你的data目录必须是完全拷贝自5.7,或者升级过程没有损坏文件。
操作步骤如下:
第一步,先停掉MySQL服务。用systemd管理的话:
systemctl stop mysqld或者老一点的init脚本:
/etc/init.d/mysql stop第二步,确认没有残留进程:
ps aux | grep mysqld有残留就kill掉,否则后面改表的时候会锁冲突。
第三步,修改配置文件/etc/my.cnf,在[mysqld]段加入跳过授权表参数:
skip-grant-tables同时为了让8.0.44能容忍旧的插件配置,建议再加一行:
mysql_native_password=ON这个参数在8.0.44里可能已经不生效(版本越新,弃用越彻底),但加上的影响不大,至少不会因为插件问题多一道坎。实测中,8.0.44里这个参数会被识别为未知变量并给出警告,但不影响跳过授权表的方式启动。
第四步,启动MySQL服务,然后不输入密码直接登录:
systemctl start mysqld mysql -uroot这个时候已经能进到MySQL命令行里了。但因为系统表还是MyISAM,你执行任何ALTER USER、CREATE USER、GRANT操作都会报错。我们需要做的是把mysql库下所有MyISAM表全部改成InnoDB。
登录后执行:
SELECT table_name, engine FROM information_schema.tables WHERE table_schema = 'mysql';这条语句会列出mysql库下所有表及它们的存储引擎。正常情况下,5.7迁移过来的环境里,user、db、tables_priv、columns_priv、procs_priv、proxies_priv、servers、func、event、slow_log、general_log这堆表应该都是MyISAM或CSV(后两个日志表默认是CSV),也有部分表可能是InnoDB(比如innodb_table_stats、innodb_index_stats)。
对所有MyISAM表执行转换,可以写一个存储过程批量操作,也可以手动一条条执行。我这边给一个比较稳妥的SQL拼接写法,不会写存储过程的直接用这个:
SELECT CONCAT('ALTER TABLE mysql.`', table_name, '` ENGINE=InnoDB;') FROM information_schema.tables WHERE table_schema = 'mysql' AND engine = 'MyISAM';这个查询的结果是一组ALTER TABLE语句,复制出来逐条执行即可。注意slow_log和general_log这两张日志表,默认引擎可能是CSV,CSV不支持转换为InnoDB,但日志表不属于授权类系统表,8.0登录时不会强制要求它们用InnoDB,所以可以跳过。
第五步,确认所有授权关键表都已经是InnoDB后,先退出MySQL,把配置文件里的skip-grant-tables注释掉,再重启服务,恢复正常登录。
这里有一个重要的细节:一定要在重启之前把skip-grant-tables去掉。否则你重启之后每次登录都不需要密码,任何人都可以直接进入,风险极高。
3.2 方案二:保留旧数据,原地重新初始化系统库(推荐)
这个方案是实际生产环境里最推荐的做法。它比方案一更干净,因为方案一只是改引擎,而如果升级过程中某些系统表的数据本身已经损坏了(比如5.7的mysql.user表某些行结构异常),光是改引擎可能不够,登录时还是会报别的错。
方案二的思路是:保留你的业务数据(也就是information_schema里除了mysql库之外的其他数据库),但把mysql库完全丢弃,让8.0.44重新生成一套全新的系统表。
操作步骤:
第一步,停掉MySQL服务,备份data目录下所有业务库的数据文件。如果你不知道业务库有哪些,先看目录结构:
ls -l /var/lib/mysql/除了mysql、performance_schema、sys、information_schema(这个不落盘)之外,其他目录基本就是你的业务库。把这些目录全部复制到备份路径:
cp -rp /var/lib/mysql/mydb /backup/mydb注意-p参数保留权限属主,否则恢复时MySQL进程可能因为文件属主不对而拒绝读取。
第二步,把旧的mysql系统库目录删掉(或者整个datadir备份后清空,再新建一个空datadir)。推荐后者,因为你无法确定哪些系统表文件还残留着:
mv /var/lib/mysql /var/lib/mysql_bak_$(date +%F) mkdir -p /var/lib/mysql chown mysql:mysql /var/lib/mysql chmod 750 /var/lib/mysql第三步,用mysqld初始化新的数据目录:
mysqld --initialize-insecure --user=mysql --datadir=/var/lib/mysql--initialize-insecure表示初始化时root账号无密码,方便后续登录。如果不想裸奔,可以用--initialize,它会在日志里生成一个临时密码,但后面登录要先去日志里找密码,稍微麻烦一点。实际运维里我经常先用insecure初始化,进去改完密码再锁权限。
第四步,启动MySQL服务,确认能正常登录:
systemctl start mysqld mysql -uroot这个时候你登录的是全新的mysql系统库,8.0.44会自动建好所有InnoDB系统表,登录流程完全正常。
第五步,把业务数据目录复制回新datadir。复制之前先确认新datadir下的路径结构:
cp -rp /backup/mydb /var/lib/mysql/mydb chown -R mysql:mysql /var/lib/mysql/mydb注意,业务库目录下如果有*.frm、*.ibd文件,拷贝回来后,MySQL需要在启动时重新识别表结构。8.0.44直接读数据字典(data dictionary)里的元数据,但如果你的业务库表是5.7版本创建的InnoDB表,8.0可以自动识别,因为InnoDB表的物理格式在5.7到8.0之间是兼容的。如果你的业务表是MyISAM,8.0也完全支持读取(前面讲过MyISAM本身没有被移除),所以不用担心。
第六步,重启MySQL,检查所有业务表是否可见:
SHOW DATABASES; USE mydb; SHOW TABLES; SELECT COUNT(*) FROM your_table;这个方法的核心逻辑就是用一个全新的8.0.44系统表环境,去承载旧业务数据,绕开升级过程中任何系统表层面的脏状态。我多次用这个方法救过线上环境,比任何修补方式都可靠。
3.3 方案三:完整逻辑迁移,适合有时间重建权限的场景
如果你的环境允许停机时间足够长,或者你手头有mysqldump导出的SQL备份,那么方案三更省心——完全抛开旧data目录,直接用SQL文件重建整个实例。
操作流程:
- 从5.7环境导出业务数据(排除mysql库和sys库):
mysqldump -uroot -p --single-transaction --set-gtid-purged=OFF --databases mydb1 mydb2 > all_business.sql初始化8.0.44数据目录(同上一步的--initialize-insecure)。
导入业务数据:
mysql -uroot -p < all_business.sql- 手动重建所有业务账号和授权,因为系统表是全新的,之前所有账号都不在了。
这个方案的缺点非常明显:权限体系需要全部重建,而且如果有大量存储过程、触发器、定时事件,导入时需要额外确认兼容性。5.7的存储过程在8.0.44里有可能因为字符集或sql_mode差异而创建失败。但优点是系统表绝对干净,后续不会再出现任何MyISAM相关问题。
4. 登录报错背后的底层逻辑:授权表被硬编码校验
4.1 MySQL 8.0的授权表读取链路
MySQL 8.0把授权信息从MyISAM表改成了内存+InnoDB双层结构。你执行登录时,服务端会经历这样一条链路:
- 客户端发送用户名、密码、目标库。
- 服务端在内存中的账号缓存里查找用户是否存在。这个缓存是从
mysql.user表加载过来的。 - 如果缓存没有命中,服务端直接去读
mysql.user表。 - 读表时,服务端会检查这个表的存储引擎是否在允许列表里。
- 8.0的允许列表只有InnoDB(实际上还会检查表的row format、collation等属性)。
- 检查不通过,返回错误。
实际代码里,这个检查是通过数据字典层完成的。MySQL 8.0引入了新的数据字典(Data Dictionary),系统表的元数据不再存放在.frm文件里,而是统一由数据字典表管理。数据字典表本身在InnoDB存储引擎下有一个独立表空间(mysql.ibd),这个文件在你5.7的data目录里是不存在的。升级后如果系统表文件还是5.7的老结构(没有对数据字典做转换),那么8.0.44启动时倒不至于直接崩溃,但访问授权表时会发现元数据映射不上,进而报错。
这也就解释了为什么单纯改default_authentication_plugin没有用——问题根本不在插件,而在系统表结构。
4.2 general_log和slow_log也需要特殊关注
有时候,系统表引擎列表里,除了mysql.user、mysql.db,还会出现mysql.slow_log和mysql.general_log。这两张表在5.7和8.0里默认都是CSV引擎,但它们的特殊性在于:CSV引擎不允许索引,也不能直接ALTER到InnoDB。
如果你在方案一里尝试转换这两张日志表,会报错:
ERROR 1067 (42000): Invalid default value for 'event_time'或者:
ERROR 1846 (HY000): Cannot change column 'event_time': used in a generated column实际上你不用管它们,因为登录校验用不到日志表。真正必须转成InnoDB的是下面这张表清单:
| 表名 | 用途 | 不转换的后果 |
|---|---|---|
| user | 用户账号和密码 | 登录直接失败 |
| db | 库级权限 | 登录后选择数据库失败 |
| tables_priv | 表级权限 | 操作具体表时报权限错误 |
| columns_priv | 列级权限 | 精细权限不可用 |
| procs_priv | 存储过程权限 | 调用存储过程失败 |
| proxies_priv | 代理用户权限 | 代理认证失败 |
| servers | 联邦服务器信息 | FEDERATED引擎不可用 |
| func | 自定义函数 | 函数注册失败 |
| event | 定时事件 | 事件调度器报错 |
建议在转换前,先只对这张表清单执行ALTER TABLE ... ENGINE=InnoDB,转换完成后再核对剩余表。多余的表里如果有MyISAM残留,大多数不影响登录,但为了干净,能转的尽量转。
4.3 为什么单独设置mysql_native_password不能解决问题
网上很多教程让你在my.cnf里加:
default_authentication_plugin=mysql_native_password这个参数在5.7时代有效,因为当时认证插件可以从配置层面全局指定。但到了8.0.44,这个参数已经被移除或者只是作为兼容参数存在。即便你用--default-authentication-plugin=mysql_native_password启动,服务端在读取mysql.user表时依然会先做存储引擎校验,MyISAM表这个坎过不去,后面的认证插件选择根本轮不到执行。
我做过一个小实验:在一台干净的8.0.44实例上,把mysql.user表改成MyISAM,然后再设置default_authentication_plugin=mysql_native_password,重启服务登录,报错依旧。所以如果你遇到了这个问题,不要浪费时间在这个参数上,直接进入系统表转换流程。
5. 升级前的防御策略:如何避免下一次踩坑
5.1 升级前必须执行的检查和备份
写到这里,我认为最有价值的部分不是报错后怎么修,而是怎么在升级前就知道会出问题。
在从5.7升级到8.0.44之前,你要先检查组件的兼容性。MySQL官方提供了一个升级检查工具mysqlcheck,在5.7版本中内置:
mysqlcheck -uroot -p --all-databases --check-upgrade这个命令会扫描所有表,并尝试检测是否有8.0不兼容的结构。如果输出里有Status: OK,说明表结构层面没有大的问题。但注意:mysqlcheck在5.7版本里对系统表的引擎检查并不严格,它不会主动提示"MyISAM系统表升级后会失败"。
更可靠的方法是手动检查mysql库下所有表的引擎:
SELECT table_name, engine FROM information_schema.tables WHERE table_schema = 'mysql' ORDER BY table_name;只要发现任何授权表(user、db、tables_priv、columns_priv、procs_priv等)是MyISAM,升级前就应该用方案一的ALTER语句把它们转成InnoDB,在5.7环境里先完成转换,再做升级。这比升级到8.0之后再处理要安全得多。
5.2 升级方法的选择:原地升级 vs 逻辑迁移
原地升级(就是直接用高版本mysqld启动旧datadir)对系统表的转换要求最高。MySQL官方提供了一条升级路径:5.7的mysqld在首次启动时会自动执行升级脚本,把系统表从MyISAM迁移到InnoDB。但这个自动迁移动作并不可靠,特别是在特殊字符集、sql_mode配置、旧数据损坏等情况下,很容易做到一半中断。
相对稳定的方式是逻辑迁移:
- 在5.7实例上导出业务数据和权限数据。
- 全新安装8.0.44。
- 手工导入数据。
- 手工重建权限。
这种方式虽然繁琐,但每一步都有回滚余地,不会出现"系统表停在半迁移状态"这种尴尬局面。
5.3 定期检查系统表健康状态
即使你不做跨大版本升级,8.0.44运行一段时间后,也可能因为异常断电、磁盘空间满、表损坏等导致系统表出问题。建议把下面这条命令写进巡检脚本:
mysqlcheck -uroot -p --system=mysql --check它能快速检查mysql库下所有表的完整性,任何存储引擎层面的异常都会在输出里显示。如果发现某个授权表报错,别等它变成登录故障,趁早修复。
6. 实操复盘:一次完整的修复记录
6.1 模拟故障环境
我做了一个模拟环境,专门复现这个问题。环境信息如下:
- 源环境:MySQL 5.7.44,CentOS 7.9,datadir路径
/var/lib/mysql - 目标环境:MySQL 8.0.44,CentOS 7.9,未初始化datadir
先把5.7的整个data目录打包复制到8.0.44环境的临时目录:
tar -czf mysql57_data.tar.gz /var/lib/mysql scp mysql57_data.tar.gz root@newserver:/tmp/在8.0.44服务器上解压:
mkdir -p /tmp/mysql57_data tar -xzf /tmp/mysql57_data.tar.gz -C /tmp/mysql57_data然后把解压后的数据目录挪到MySQL 8.0.44的默认datadir:
rm -rf /var/lib/mysql/* cp -rp /tmp/mysql57_data/var/lib/mysql/* /var/lib/mysql/ chown -R mysql:mysql /var/lib/mysql启动服务:
systemctl start mysqld查看日志:
tail -100 /var/log/mysql/error.log日志里能看到:
[ERROR] [MY-011036] [Server] Table 'mysql.user' is not in the right format. [ERROR] [MY-010946] [Server] Failed to initialize ACL. [ERROR] [MY-010954] [Server] Aborting有些场景下服务端直接Aborting,有些场景服务端能启动但无法登录,区别在于日志表是否能被正常加载。我的模拟环境属于后者——服务端进程存活,但任何登录尝试都失败。
6.2 使用skip-grant-tables完成系统表转换
参照前面方案一的步骤,我在/etc/my.cnf中添加:
[mysqld] skip-grant-tables重启MySQL:
systemctl restart mysqld mysql -uroot进到MySQL命令行后,执行引擎查询:
SELECT table_name, engine FROM information_schema.tables WHERE table_schema='mysql';结果里user、db、tables_priv等表全部显示MyISAM。然后拼接ALTER语句:
SELECT CONCAT('ALTER TABLE mysql.`', table_name, '` ENGINE=InnoDB;') FROM information_schema.tables WHERE table_schema='mysql' AND engine='MyISAM' AND table_name NOT IN ('slow_log', 'general_log');执行输出的每一条ALTER语句,直到所有授权表都变成InnoDB。这个过程在模拟环境里大概2分钟跑完,因为mysql库表数据不大。
改表结束后,我又额外执行了一条刷新授权缓存命令:
FLUSH PRIVILEGES;这条命令在skip-grant-tables模式下可以执行,但只影响内存里的缓存,不影响磁盘结构。不过执行一下无妨,确保下一步登录时ACL缓存不是旧状态。
然后退出,把my.cnf里的skip-grant-tables注释掉,重启服务:
systemctl restart mysqld mysql -uroot -p这次正常轮到密码验证了。输入原账号密码,成功登录。
6.3 模拟环境的差异化表现:skip-grant-tables模式下的坑
在skip-grant-tables模式里,有一个行为需要特别提醒:在这种模式下,任何账号只要用户名匹配就能登录,密码验证被完全绕过。所以如果你在会话里执行了ALTER USER 'root'@'localhost' IDENTIFIED BY 'newpassword';,有时不生效,因为服务端处于skip模式时不会更新认证相关的缓存。
正确的做法是:不要在skip模式下尝试改密码,把所有系统表引擎转换做完之后,退出、恢复配置、正常登录,再用ALTER USER修改密码。如果你在skip模式下改密码,而且服务端竟然成功执行了,那么等正常重启后,这个新密码可能有效,也可能因为缓存和磁盘数据不同步而显示为旧密码。为了避免这种混乱,强烈建议skip模式只做表结构变更,不做任何账号和密码操作。
7. 常见问题与排查技巧实录
7.1 常见问题速查表
| 症状 | 可能原因 | 处理方式 |
|---|---|---|
| 登录报错包含MyISAM、system tables关键词 | 系统表未升级到InnoDB | skip-grant-tables进入,批量ALTER表引擎 |
| 升级后服务启动即崩溃,日志显示ACL初始化失败 | mysql.user表损坏或格式不兼容 | 备份业务库后,重新初始化datadir |
| 登录成功但SELECT业务数据表报错 | 业务表本身结构或数据文件损坏 | 用mysqlcheck检查业务库,必要时从备份恢复个别表 |
| 执行ALTER TABLE mysql.user ENGINE=InnoDB时报权限错误 | skip-grant-tables未生效 | 确认配置文件无语法错误,重启服务 |
| 修改密码后依然登录失败 | skip模式下改密码导致缓存不一致 | 重启服务,再次走正常登录流程,重新ALTER USER |
| 8.0.44登录报错同时出现deprecated plugin提示 | mysql_native_password被标记弃用 | 不影响登录前提下忽略;若影响,改用caching_sha2_password插件 |
7.2 一个容易忽视的细节:默认认证插件切换
升级完成后,登录虽然成功了,但在8.0.44里,新创建的用户默认使用caching_sha2_password认证插件。如果你的客户端是旧版本(比如5.7时代的PHP mysqli扩展、老版本Navicat、部分JDBC驱动),可能出现"能连接但认证失败"的情况。这时需要在MySQL端给对应账号修改插件:
ALTER USER 'appuser'@'%' IDENTIFIED WITH caching_sha2_password BY 'password';或者允许旧插件(除非必要,不建议):
ALTER USER 'appuser'@'%' IDENTIFIED WITH mysql_native_password BY 'password';注意,mysql_native_password插件在8.0.44里仍然存在,但官方已经不推荐。新客户端建议直接适配caching_sha2_password。
7.3 备份文件里的MyISAM表也要检查
还有一类隐蔽场景:你从5.7导出了mysqldump备份文件,然后在8.0.44里导入。这种情况下系统表是新的InnoDB,登录没问题,但备份文件里的业务表可能带有ENGINE=MyISAM创建语句。8.0.44支持这些表,所以能正常导入,但后续如果在这些表上做ALTER操作,可能遇到一些与MySQL 8.0严格模式相关的兼容性问题。建议导入后对关键表执行一次:
OPTIMIZE TABLE your_table;这能重新整理表碎片和索引,同时在8.0.44里把这些表彻底纳入新的数据字典管理。
7.4 如何快速判断系统表是否需要修复
提供一个快速判断命令,不需要进入MySQL:
ls -l /var/lib/mysql/mysql/*.ibd | head -20如果mysql目录下没有user.ibd、db.ibd这些文件,而只有user.MYD、user.MYI,说明系统表还是MyISAM物理文件。这种状态下,8.0.44几乎必然会出登录问题。
如果mysql目录下只有mysql.ibd这个单独文件(8.0的数据字典表),并且能看到user.ibd、db.ibd等单独的InnoDB表空间文件,说明系统表已经正常在InnoDB下运行。
7.5 不建议用的两种"土办法"
网上有些人会建议直接把5.7的data目录里的user.MYD和user.MYI文件删掉,让8.0重建。这个方案我强烈反对,因为删掉授权表文件之后,8.0.44在启动时可能直接宕掉,而不是自动重建。MySQL 8.0的系统表重建只能通过初始化流程完成,不支持无文件自动创建。
还有人建议用REPAIR TABLE mysql.user来修复,这在MyISAM表时代有效,但8.0.44的授权表已经是InnoDB引擎,而且REPAIR在InnoDB表上只是重建统计信息,不能解决存储引擎不匹配的问题。所以看到这个建议直接跳过。
8. 修复完成后的验证清单
系统表转换完成、登录恢复正常之后,我建议做一轮完整的验证,避免隐藏问题影响后续业务。
第一,检查所有系统表引擎:
SELECT table_name, engine FROM information_schema.tables WHERE table_schema='mysql' AND engine NOT IN ('InnoDB', 'CSV', 'MyISAM');正常情况下这个查询返回空集。如果有返回,说明还有残留表需要处理。
第二,验证权限操作:
CREATE USER 'test_upgrade'@'localhost' IDENTIFIED BY 'Temp1234!'; GRANT SELECT ON *.* TO 'test_upgrade'@'localhost'; SHOW GRANTS FOR 'test_upgrade'@'localhost'; DROP USER 'test_upgrade'@'localhost';这一串操作在旧系统表环境下都是报错的,现在应该全部正常执行。
第三,检查caching_sha2_password插件是否可用:
SELECT plugin, status FROM information_schema.plugins WHERE plugin_name = 'caching_sha2_password';状态应为ACTIVE。如果显示DISABLED,需要检查配置文件里是否有skip_ssl或者插件加载异常。
第四,确认数据字典状态:
SELECT * FROM performance_schema.dict_object_count;或者用官方更直接的命令:
mysqlcheck -uroot -p --check-upgrade --all-databases输出里应该看不到任何error级别的内容。
9. 个人实操中的几点体会
MySQL 8.0.44对系统表存储引擎的严格要求,本质上是把数据库的基础设施推向更可靠的方向。MyISAM作为系统表引擎的服役年限足够久了,它简单高效,但不适合承载认证和权限这类高并发、强一致性的关键数据。InnoDB的事务能力和崩溃恢复能力,才是系统表在断电、磁盘故障等场景下保命的根本。
我在处理过多起类似问题后,团队里已经形成了一条铁律:任何跨大版本升级之前,先检查系统表引擎,先转换,再升级,不要指望升级工具帮我们自动处理。这条铁律帮我挡掉了后面很多次半夜紧急修复。
再分享最后一个细节。在8.0.44环境里,如果你用mysqldump导出的SQL文件不含SET @@GLOBAL.GTID_PURGED这类GTID语句,导入新实例后,主从复制的GTID集合可能对不上,这在搭建从库时特别容易踩。所以如果你升级之前的主库开着GTID,导出时建议加上--set-gtid-purged=OFF,然后手动设置GTID_PURGED或者直接跳过GTID约束,否则复制同步时会碰到幺蛾子。
这次的坑从登录报错背后的系统表引擎问题出发,一路牵出了升级路径选择、数据字典机制、认证插件兼容性等多个连环话题。如果你在升级8.0.44时也遇到了类似的MyISAM报错,按文中的方案一或方案二操作,基本都能恢复。如果操作过程中还有其他奇怪报错,欢迎带着日志片段来讨论。