☰
MySQL密码遗忘怎么办?Linux虚拟机下绕过授权表重置root密码全攻略
2026/10/21 8:06:35 网站建设 项目流程

上周登录虚拟机整理业务库,随手敲了自以为一直没变的 MySQL root 密码,结果连续三次 Access denied,MySQL 直接把我会话掐断。同事猜的密码也全试了一遍,依然不对。这台虚拟机倒是留过快照,但快照停在三个月前——回滚意味着这三个月落库的数据全部蒸发,代价比忘密码大多了。最终只能走“绕过授权表”的路子把密码重置掉。整个过程不复杂,但里面值得记的细节相当多:版本语法不一致、socket 目录缺失、远程连不上、内存不够导致服务反复挂……这些坑我几乎全踩了一遍。如果你也在一台 Linux 虚拟机上遇到 MySQL 密码遗忘,这篇文章从原理到操作、从排错到预防都会覆盖到,照着走能省不少时间。

1. 动手之前先确认三件事:版本、服务方式、数据安全

很多人拿到“忘记密码”第一反应就是搜教程,然后复制一行UPDATE mysql.user SET Password=PASSWORD(...)去执行。结果通常会收到Unknown column 'Password'。原因很简单:那个字段在 MySQL 5.6 时代就改掉了。所以第一步不是重置,而是摸清楚环境。

1.1 先分清你跑的是 MySQL 5.7、8.0 还是 MariaDB

虚拟机上的 MySQL 来源很杂,有 apt 装的原版 MySQL,有 yum 装的 MariaDB,还有人直接把官方 tar 包解压出来用。不同分支和版本的授权表结构、密码字段、SQL 语法都不一样。

用这几条命令快速确认:

mysql -V mysqld --version sudo systemctl status mysql

输出里会直接写明版本号。如果是 MariaDB,一般显示MariaDB字样;如果是 MySQL 8.0,重置密码的字段是authentication_string,5.7 也差不多,但 5.6 及更早版本的Password字段已经不存在了。这个信息直接决定你后面 SQL 怎么写,别跳过。

1.2 确认服务是通过 systemd 还是 mysqld_safe 管理的

Ubuntu/Debian 上服务名一般是mysql,CentOS/RHEL 上通常是mysqld,但也有源码编译安装后用mysqld_safe直接拉起的。这会影响你后续“停服务”和“改配置重启”时的命令。

# 查看服务单元 sudo systemctl list-unit-files | grep -E 'mysql|maria' # 查看当前进程是怎么起来的 ps -ef | grep mysql

看到mysqld_safe字样,说明服务是通过这个安全启动脚本管理的;看到/usr/sbin/mysqld直接运行,通常是 systemd 管理。两种方式下,“跳过授权表”的启动方法略有差异,我在第 2 章分别给出。

这一步还顺带确认了一个前提:你能拿到系统 Shell 并且有 root 或 sudo 权限。如果连系统密码也忘了,那属于虚拟机系统登录问题,需要关机关机挂载磁盘修改,不在本文讨论范围。

1.3 备份永远是第一优先级

只改密码为什么还要备份?因为操作授权表是高风险动作。比如一条UPDATE mysql.user SET authentication_string='' WHERE user='root'如果漏了 host 条件,可能把所有 root 记录的插件和 host 一起改坏,最坏情况是 MySQL 直接拒绝所有连接。

在虚拟机上,我的习惯是双保险:先打快照,再做数据目录文件级备份。快照的意义在于可以快速回到操作前的状态,哪怕后面把授权表搞坏,一秒钟回滚;文件级备份的意义在于快照可能因为虚拟机平台原因失效,或者你想在另一台机器上恢复数据。

数据目录备份要在 MySQL 停机的状态下做,否则在线拷贝可能得到不一致状态:

sudo systemctl stop mysql sudo cp -rp /var/lib/mysql /var/lib/mysql.bak.$(date +%F) sudo systemctl start mysql

cp -rp里的-p很重要,它保住目录和文件的属主与权限。备份完成后确认一下目录大小和磁盘剩余空间,避免备份到一半把磁盘塞满。

1.4 别急着回滚快照:先想清楚数据可丢失程度

虚拟机的快照确实是“后悔药”,但不是万能药。我见过有同事遇到任何问题第一反应就是回滚,完全忽略一个重要事实:回滚会丢掉自快照以来所有的数据变化。如果这台虚拟机只是测试机,快照回滚没什么问题;如果上面跑着正式业务或唯一一份数据库,回滚造成的灾难往往比忘密码严重得多。

所以我的判断标准很简单:数据可丢,回滚最快;数据不可丢,老老实实按本文流程重置密码。这台机器如果确实有定期备份,回滚也是一种思路,但多数情况下重置密码更稳。

2. 绕过授权验证:原理、安全边界、两条启动路径

2.1 密码到底是在哪里被校验的

用户登录 MySQL 时,服务端会去mysql.user系统表里查匹配的账号记录,比对host、plugin、authentication_string这些字段。--skip-grant-tables参数的作用,是让服务启动时不加载授权表,相当于把校验环节整个跳过。这时候任何人通过本机 socket 连接,都是以 root 身份直接进入,不需要密码。

需要强调的是,它只是跳过了校验,并没有清空授权表。数据本身不会丢,你仍然可以读取mysql.user表,也可以修改它。正因如此,这个模式是一把双刃剑:能让你无密码进入,同时也意味着如果服务监听了外部网络,任何人都能无密码连进你的数据库。

所以实际启动时必须同时加上--skip-networking,让 MySQL 只监听本机 socket,不监听 TCP 端口。可以类比成:大门警卫撤了,但你同时把围墙电梯全部锁定,外面的人根本走不进来。

2.2 路径 A:mysqld_safe 临时拉起

这是最经典的做法。先把服务停掉,再用mysqld_safe手动拉起一个跳过授权的实例:

sudo systemctl stop mysql sudo mysqld_safe --skip-grant-tables --skip-networking &

进程起来后,直接不带密码登录:

mysql -uroot

如果报Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock',说明 socket 路径不对或者目录缺失。可以从配置里查实际路径:

sudo grep -R "socket" /etc/mysql/my.cnf /etc/mysql/mysql.conf.d/ /etc/my.cnf 2>/dev/null

然后显式指定 socket 登录:

mysql -uroot -S /var/run/mysqld/mysqld.sock

值得注意的是,新版 MySQL 在某些发行版里已经不提供mysqld_safe脚本,直接提示command not found。遇到这种情况不要纠结,转路径 B。

2.3 路径 B:修改配置文件后走 systemctl start

如果 mysqld_safe 不存在,或者你更习惯用 systemd 统一管理,就在配置文件[mysqld]段下临时加两行:

[mysqld] skip-grant-tables skip-networking

配置文件的位置因发行版而异:Ubuntu/Debian 通常在/etc/mysql/mysql.conf.d/mysqld.cnf,CentOS/RHEL 通常在/etc/my.cnf。改完后执行:

sudo systemctl start mysql

然后照旧mysql -uroot无密码登录。这个方式的本质和路径 A 完全一样,只是通过配置文件把参数传给 mysqld。好处是可复用、方便用 systemctl 管理;坏处是一旦操作完忘记注释掉这两行,下次重启会继续裸奔,安全隐患非常大。

2.4 两条路径怎么选

我的建议是:优先使用配置文件方式。理由有几点:第一,不依赖mysqld_safe脚本是否存在;第二,启动、停止、查看状态都可以用systemctl,行为可预期;第三,恢复时只需要删掉两行再执行 restart,干净利落。mysqld_safe方式虽然少改一个文件,但它是前台脚本,放后台后如果终端关闭可能收到挂断信号导致进程退出,还得配合nohup,实际体验没那么顺手。

当然,如果你只是想在不清空现有配置的前提下临时试一下,路径 A 更轻量。熟练之后两条路都能走,第一次操作建议走 B。

3. 密码重置全流程:从停机到验证

3.1 停机与备份

正式操作前,先执行:

sudo systemctl stop mysql sudo df -h sudo cp -rp /var/lib/mysql /var/lib/mysql.bak.$(date +%F)

先df -h确认/var/lib所在分区有足够空间。很多人忽略这块,结果备份到一半磁盘满了,MySQL 起不来,数据目录还可能写出半截文件。尤其数据量大的虚拟机,几个 GB 的库备份出来就占几个 GB,提前确认空间非常必要。

3.2 以跳过授权模式启动

按 2.3 节方式,在[mysqld]段加上skip-grant-tables和skip-networking,然后:

sudo systemctl start mysql sudo systemctl status mysql

看到active (running)说明已经进入跳过授权状态。如果显示失败,不要着急重试,直接跳到第 4 章排查日志。

3.3 登录并执行密码重置 SQL

无密码登录:

mysql -uroot

进入客户端后,先看 root 账号的分布情况:

USE mysql; SELECT user, host, plugin, authentication_string FROM user WHERE user='root';

常见输出会包含localhost、127.0.0.1、::1等多个 host,需要逐个处理。接下来执行一个小但关键的步骤:

FLUSH PRIVILEGES;

这一步很多教程不讲,但在--skip-grant-tables模式下权限表并未加载,直接执行ALTER USER可能出现ERROR 1290或修改不生效。先刷新一下,让本次会话知道权限表已经可写了,后面才顺。

然后执行修改密码。MySQL 5.7 和 8.0 都推荐用ALTER USER:

ALTER USER 'root'@'localhost' IDENTIFIED BY '你的新密码'; ALTER USER 'root'@'127.0.0.1' IDENTIFIED BY '你的新密码'; ALTER USER 'root'@'::1' IDENTIFIED BY '你的新密码';

如果希望兼容较老的客户端程序,MySQL 8.0 可以显式指定mysql_native_password插件:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的新密码';

如果你执行时遇到ERROR 1819 (HY000): Your password does not satisfy the current policy requirements,说明 validate_password 密码强度策略在起作用。可以在当前会话临时调低:

SET GLOBAL validate_password.policy = LOW; SET GLOBAL validate_password.length = 6;

之后再执行ALTER USER。重置完成后记得把强度参数调回原来的值。如果系统提示Unknown system variable 'validate_password.policy',说明当前环境没有启用该组件,忽略即可。

对于多个 host 的 root 账号,也可以统一用一条 UPDATE 清空密码再加 ALTER,但我不推荐在 skip 模式下执行大范围 UPDATE,原因见第 5 章翻车复盘。务必逐个 SELECT 确认后再修改对应行。

3.4 恢复配置、重启、验证

密码都改完后,退出客户端:

exit;

然后删除或注释[mysqld]段里临时加的两行配置,重启服务:

sudo systemctl restart mysql

最后验证:

mysql -uroot -p

输入新密码能进入,说明重置成功。这里有一个小经验:验证登录时不要用mysql -uroot -p'密码'这种一行式写法,除非你非常确认终端历史记录不会被别人看到。交互式输入密码虽然慢,但安全很多。

4. 最容易翻车的四个环节:排查链路逐一过

4.1 服务起不来:先看日志,再谈其他

跳过授权模式下systemctl start mysql,如果几秒后状态变成 failed,第一件事一定是看错误日志,而不是盲目重启或删除数据目录。

日志位置按发行版不同有差异:

平台日志路径
Ubuntu/Debian/var/log/mysql/error.log
CentOS/RHEL/var/log/mysqld.log
systemd 采集journalctl -u mysql或journalctl -u mysqld

最经典的报错是:

mysqld_safe Directory '/var/run/mysqld' for UNIX socket file don't exists

原因是/var/run/mysqld目录在停止服务时被清了,而启动脚本没有自动创建。解决办法:

sudo mkdir -p /var/run/mysqld sudo chown mysql:mysql /var/run/mysqld

另一个常见问题是数据目录属主不对,日志里会有Permission denied或Can't open the mysql.plugin table。矫正属主:

sudo chown -R mysql:mysql /var/lib/mysql

这两个问题在虚拟机里尤其常见,因为快照回滚后文件属主有可能被重置成 root。

4.2 登录上了,但 SQL 执行报错

跳过授权模式登录本身不难,难的是 SQL 能不能一次通过。我列一下高发报错和对应处理:

报错一:Unknown column 'Password'

说明你抄到了老教程。MySQL 5.7 之后密码字段叫authentication_string,没有Password字段。

报错二:Unknown system variable 'validate_password.policy'

不用慌张,这只是没装 validate_password 组件而已,忽略这行设置继续执行就行。

报错三:ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement

这就是我在 3.3 节强调要先执行FLUSH PRIVILEGES的原因。重新登录,先执行FLUSH PRIVILEGES;,再执行ALTER USER,基本就能过去。

4.3 改完密码远程连不上

重置好密码后,很多人想立刻从宿主机或其他机器连过去,结果还是Access denied。这时候按顺序查三处:

1. user 表里的 host 范围

如果 mysql.user 里根本没有'root'@'%'这条记录,远程登录无论如何都会失败。需要单独建立远程账号:

CREATE USER 'root'@'%' IDENTIFIED BY '你的新密码'; GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES;

但注意,开放 root 从任意 IP 登录本身就是很大的安全隐患。生产环境我更建议新建一个专用维护账号,把 host 限制成你的固定 IP,而不是直接放开 root。

2. bind-address 配置

如果配置文件里有bind-address = 127.0.0.1,MySQL 只监听本机回环地址,外部连接当然被拒。改成0.0.0.0并重启。

3. 虚拟机防火墙

宿主机之外还有虚拟机的系统防火墙。查看并放行 3306 端口:

# Ubuntu/Debian sudo ufw status sudo ufw allow 3306/tcp # CentOS/RHEL sudo firewall-cmd --list-all sudo firewall-cmd --permanent --add-port=3306/tcp sudo firewall-cmd --reload

最后确认端口确实在监听:

ss -tlnp | grep 3306

如果看到127.0.0.1:3306说明还是只监听本机,bind-address 没改对。

4.4 虚拟机资源吃紧导致的不稳定

很多虚拟机内存只分到 1GB 或 2GB。skip-grant-tables模式虽然不加载授权表,但 InnoDB buffer pool 仍然会初始化,内存不够时服务可能起来没多久又被 OOM killer 杀掉。现象是systemctl start mysql后 status 一度 running,再过几秒变成 dead。

排查:

dmesg | tail -30

看到Out of memory字样基本可以确认是资源问题。两种解法:一是临时调大虚拟机内存再重启;二是在启动命令里临时降低 buffer pool:

sudo mysqld_safe --skip-grant-tables --skip-networking --innodb-buffer-pool-size=64M &

密码重置完成后,记得把内存配置恢复成正常值。

如果以上步骤全做对了,服务还是起不来,虚拟机场景下还有一个高效兜底方案:关机,把这块系统盘挂载到另一台临时虚拟机上,从外部修改配置或数据目录,再挂回来启动。这也是我反复强调先备份数据目录的原因——有备份在,永远有退路。

5. 重置之后:收尾动作与防止“再来一次”

5.1 收尾清单

密码重置成功不是终点,我每次都会按下面清单过一遍:

  • 确认[mysqld]下的skip-grant-tables和skip-networking已经删除或注释。
  • 重启后执行mysql -uroot -p,确认默认的密码策略恢复正常。
  • 检查mysql.user表,确认 root 账号的 host 范围符合预期,多余的 host 记录删掉。
  • 检查系统日志和 shell history,确认没有把密码明文留在容易暴露的位置。
  • 给虚拟机打一个新快照,标签写清楚“MySQL 密码已重置”,方便以后回滚到安全状态。
  • 如果刚才临时调低了 validate_password 策略,恢复原值。

5.2 把“忘密码”变成小概率事件

几个亲测有用的习惯:

  • 用密码管理器统一登记数据库密码,服务器密码、root 密码、业务账号密码分条记录。
  • 在虚拟机本机使用mysql_config_editor保存 root 的本地登录路径:
mysql_config_editor set --login-path=local --host=localhost --user=root --password

之后执行mysql --login-path=local就不需要交互输密码。注意这个凭证文件默认权限是 600,只适合本机维护使用,绝不能把它同步到远程或作为应用连接凭证。

  • 业务系统一律使用独立账号,root 只留本机维护用,避免业务侧操作直接命中超级权限。

5.3 一次真实翻车复盘

几年前给一台虚拟机重置 root 密码时,我没有先备份数据目录,也觉得ALTER USER是很轻的操作。执行时在 skip-grant-tables 模式下先写了一条UPDATE mysql.user SET authentication_string='' WHERE user='root',本想清空当前 root 的密码,结果 host 条件漏掉,把所有 root 记录全部更新了一遍,把 plugin 字段也覆盖成了默认。重启后 MySQL 直接拒绝所有本地连接,场面一度失控。最后是从另一台临时虚拟机挂载原磁盘,再花了大半天追 binlog 才恢复。

那次之后我给自己立了三条铁律:第一,任何对mysql.user表的 UPDATE,执行前必须先 SELECT 看清行数和 host;第二,凡是 skip 模式下修改密码,一定先FLUSH PRIVILEGES再ALTER USER;第三,只要动授权表,停机备份或快照至少要有一样。

最后补充一个可能对你有用的小技巧。如果你手头恰好有一个具备 SUPER 或 SYSTEM_USER 权限的业务账号能正常登录,只是没有 root 密码,其实不需要走这么重的重启流程,直接在该账号会话里执行授权命令就能重置或其他管理操作。只是这个思路对账号权限要求比较高,普通场景下还是按上面的通用流程处理最稳。把这条记在这里,算是给以后需要的人留个更轻量的入口。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询