MySQL启动失败且日志显示“Table 'mysql.user' doesn't exist”等,表明权限表物理损坏,需先用--skip-grant-tables和--skip-networking启动,再据引擎选myisamchk(MyISAM)或--initialize-insecure(InnoDB/8.0+)修复,并运行mysql_upgrade --force补全结构。

MySQL 启动失败且错误日志里出现 Table 'mysql.user' doesn't exist 或 Incorrect information in file: './mysql/user.MYI',基本可以断定是权限表物理损坏——这不是配置或密码问题,不能靠改密码或重启解决,必须先让 mysqld 能跑起来,再修表结构。
确认是否真为权限表损坏而非其他启动故障
别一上来就修表。先看错误日志最末几行(sudo tail -n 30 /var/log/mysqld.log),只在出现以下任一情况时才走权限表修复路径:
-
Table 'mysql.user' doesn't exist或Can't find file: './mysql/user.frm' -
Incorrect key file for table './mysql/user.MYI'(MyISAM 场景) -
InnoDB: Database page corruption且紧跟着提到mysql/下的表(如user、db) - 启动后执行
SHOW DATABASES;只返回information_schema,没有mysql库
如果日志里是 Permission denied(如对 binlog.index)、Address already in use 或 unknown variable,那就不是权限表问题,得去查端口、权限或配置文件。
跳过权限验证启动 mysqld 的正确姿势
这是所有修复的前提:不进得去,就什么都干不了。但加错参数或漏配套项,要么进不去,要么留下严重安全风险。
- 必须同时加两个参数:
--skip-grant-tables和--skip-networking;只加前者,MySQL 仍会监听 3306 并允许远程无认证连接 - 不能用
service mysql restart—— 它可能残留旧进程。要先systemctl stop mysqld,再用完整命令启动:mysqld --skip-grant-tables --skip-networking --datadir=/var/lib/mysql(路径按实际调整) - 启动后立刻验证:
mysql -u anyuser -e "SELECT USER();"应输出anyuser@localhost;若仍报Access denied,说明参数没生效或 mysqld 没真正用上你传的参数 - MySQL 8.0+ 中
--skip-grant-tables仍可用,但mysql.user已由数据字典管理,此时更推荐直接用--initialize-insecure重建系统库
针对不同存储引擎选择修复方式
修法完全取决于 mysql.user 用的是 MyISAM 还是 InnoDB——看版本和初始化方式,不能混用。
- MySQL 5.7 及更早,默认 MyISAM:
myisamchk -r /var/lib/mysql/mysql/user.MYI(必须停服务)。若报key file is old,加-o参数重试:myisamchk -o -r /var/lib/mysql/mysql/user.MYI - MySQL 5.7 启用了
innodb_file_per_table,或 MySQL 8.0+:mysql.user是 InnoDB 表,REPAIR TABLE无效,强行执行会报Storage engine doesn't support repair;此时应优先尝试innodb_force_recovery=1启动,成功后再导出权限数据,或直接用--initialize-insecure - 8.0+ 数据字典损坏(
mysql.user查不到、SHOW TABLES空):mysqld --initialize-insecure --datadir=/var/lib/mysql是最稳妥方案,它生成全新、结构正确的mysql库,且不碰业务库的ibd文件 - 无论哪种方式,修完都必须运行
mysql_upgrade --force -u root补全字段、索引和兼容性结构,否则后续 GRANT 或用户登录仍可能失败
手动重建权限表时最容易忽略的细节
很多人导出旧 mysql.user 再导入新表,结果还是登不上——问题不在数据,而在结构错位或字段语义变更。
- MySQL 5.7 的
user表有Password字段,8.0+ 只有authentication_string;直接 INSERT 会丢认证信息 - 字段顺序必须严格匹配目标版本的
CREATE TABLE输出,尤其是新增字段如password_last_changed、account_locked,漏掉会导致权限逻辑异常 - 不要手写
CREATE TABLE mysql.user—— 字段默认值、索引、注释都极易出错;应从同版本干净实例导出结构:mysqldump --no-data --skip-triggers mysql > mysql_struct.sql - 修复后务必执行
FLUSH PRIVILEGES;,哪怕在--skip-grant-tables模式下也有效;不刷,内存缓存还是旧的空权限状态
真正麻烦的不是修表本身,而是修完忘了验证 plugin 和 authentication_string 是否匹配当前认证方式——比如 plugin='auth_socket' 却用密码连,或者 authentication_string 是空但没设 mysql_native_password,这些都会让 root 登录持续报 ERROR 1045。


















