看到 error 3118 (hy000): access denied for user 'root'@'localhost' (account is locked) 才是明确信号;若仅报 access denied 未提“account is locked”,则大概率是插件不兼容或密码过期。

怎么确认账号真被锁了,不是密码输错或服务没起?
看到 ERROR 3118 (HY000): Access denied for user 'root'@'localhost' (account is locked) 才是明确信号;如果只报 Access denied 但没提 “account is locked”,大概率是插件不兼容或密码过期。先用已有高权限账号连上,执行:
SELECT User, Host, account_locked, password_expired, plugin FROM mysql.user WHERE User = 'root';
重点看三列:account_locked 为 'Y' 才算真锁定;password_expired 为 'Y' 会阻止首次登录;plugin 是 caching_sha2_password 则老客户端(如 Navicat 旧版、PHP 7.4 mysqli)可能连不上——这三项常共存,但错误提示只暴露一个。
为什么不能直接 UPDATE mysql.user + FLUSH PRIVILEGES?
MySQL 8.0 在 --skip-grant-tables 模式下禁用所有权限相关语句,执行 UPDATE mysql.user 或 FLUSH PRIVILEGES 会报错:ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement。手动改表不触发内部状态同步,重启后还可能被覆盖,风险极高。
FLUSH PRIVILEGES 只重载权限缓存,不改变 account_locked 运行时状态。锁定和过期字段写在 mysql.user 表里,但服务进程启动后会缓存并校验,单纯刷新权限无效。
用 --init-file 安全解锁(推荐生产环境)
比 --skip-grant-tables 更稳妥:不跳过权限系统,只在启动时执行一条 SQL,避免误操作风险。步骤如下:
- 创建临时 SQL 文件,例如
/tmp/unlock-root.sql,内容严格为两行(注意空格和引号):ALTER USER 'root'@'localhost' ACCOUNT UNLOCK;<br>ALTER USER 'root'@'%' ACCOUNT UNLOCK;
- 确保文件可读:
sudo chown mysql:mysql /tmp/unlock-root.sql - 停服务:
sudo systemctl stop mysql - 用 init-file 启动:
sudo mysqld --user=mysql --init-file=/tmp/unlock-root.sql & - 等几秒后检查端口:
sudo ss -tlnp | grep :3306,有输出即成功 - 立刻验证:
mysql -u root -p,应能登录且不再报锁 - 杀掉该临时实例:
sudo kill $(pgrep mysqld),再正常启服务:sudo systemctl start mysql
注意:ACCOUNT UNLOCK 中间必须有空格,写成 ACCOUNTUNLOCK 或 ACCOUNT UNLOK 都会报 ERROR 1064。
解锁后必须立刻检查 plugin 和密码状态
解锁只是恢复登录资格,不代表能用旧工具连上。MySQL 8.0 默认用 caching_sha2_password,很多老客户端不支持。进库后立刻查插件:
SELECT User, Host, plugin FROM mysql.user WHERE User = 'root';
若需兼容,改回传统插件:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的新密码';
别漏掉 'root'@'%'(如果存在),否则远程仍连不上。同时确认 account_locked = 'N' 和 password_expired = 'N' 都已生效——最易忽略的是:只修了 localhost 却忘了 %,结果换台机器又连不上。











