拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MySQL 8.0.44升级登录报错1805:系统表MyISAM转InnoDB修复指南

MySQL 8.0.44升级登录报错1805:系统表MyISAM转InnoDB修复指南

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文件重建整个实例。

操作流程:

  1. 从5.7环境导出业务数据(排除mysql库和sys库):
mysqldump -uroot -p --single-transaction --set-gtid-purged=OFF --databases mydb1 mydb2 > all_business.sql
  1. 初始化8.0.44数据目录(同上一步的--initialize-insecure)。

  2. 导入业务数据:

mysql -uroot -p < all_business.sql
  1. 手动重建所有业务账号和授权,因为系统表是全新的,之前所有账号都不在了。

这个方案的缺点非常明显:权限体系需要全部重建,而且如果有大量存储过程、触发器、定时事件,导入时需要额外确认兼容性。5.7的存储过程在8.0.44里有可能因为字符集或sql_mode差异而创建失败。但优点是系统表绝对干净,后续不会再出现任何MyISAM相关问题。

4. 登录报错背后的底层逻辑:授权表被硬编码校验

4.1 MySQL 8.0的授权表读取链路

MySQL 8.0把授权信息从MyISAM表改成了内存+InnoDB双层结构。你执行登录时,服务端会经历这样一条链路:

  1. 客户端发送用户名、密码、目标库。
  2. 服务端在内存中的账号缓存里查找用户是否存在。这个缓存是从mysql.user表加载过来的。
  3. 如果缓存没有命中,服务端直接去读mysql.user表。
  4. 读表时,服务端会检查这个表的存储引擎是否在允许列表里。
  5. 8.0的允许列表只有InnoDB(实际上还会检查表的row format、collation等属性)。
  6. 检查不通过,返回错误。

实际代码里,这个检查是通过数据字典层完成的。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配置、旧数据损坏等情况下,很容易做到一半中断。

相对稳定的方式是逻辑迁移:

  1. 在5.7实例上导出业务数据和权限数据。
  2. 全新安装8.0.44。
  3. 手工导入数据。
  4. 手工重建权限。

这种方式虽然繁琐,但每一步都有回滚余地,不会出现"系统表停在半迁移状态"这种尴尬局面。

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关键词系统表未升级到InnoDBskip-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报错,按文中的方案一或方案二操作,基本都能恢复。如果操作过程中还有其他奇怪报错,欢迎带着日志片段来讨论。

返回列表