ARTICLE DETAIL

资讯详情

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

MySQL root密码忘记怎么办?三种重置方法汇总(含8.0/MariaDB)

MySQL root密码忘记怎么办?三种重置方法汇总(含8.0/MariaDB) 凌晨两点接到电话说测试环境MySQL的root密码被改了谁也登不进去。这种场景我遇到过不止一次大多数人第一反应是重装数据库其实完全不用。MySQL的root密码丢失本质上只是认证层的问题数据文件、表结构、存储过程都还在原地只要绕过认证进去把密码改回来就行。这篇文章就是围绕这件事写的核心就三招skip-grant-tables跳过权限表、init-file初始化文件、以及直接操作授权表的兜底方案。Linux和Windows都会讲到MySQL 5.7、8.0和MariaDB也会区分开。无论你是刚接手别人留下的服务器还是自己把密码改忘了照下面的步骤走一遍就能解决。1. 先理清原理密码到底存在哪重置为什么不会丢数据1.1 密码存储机制与“忘记密码”的本质MySQL的用户账号信息包括用户名、主机、认证插件和密码哈希都存储在系统数据库mysql的user表里。root账号也不例外。你每次用密码登录服务端做的事情就是把输入密码做哈希计算再和mysql.user表中authentication_string字段里保存的哈希值比对一致就放行不一致就报ERROR 1045 (28000): Access denied。搞清楚这一点“忘记密码”这个问题就简化成我们不记得明文密码所以无法通过认证但数据本身没有损坏。重置密码的本质是绕过认证层进入数据库然后把mysql.user里的密码哈希替换成新密码的哈希。整个过程不涉及业务库的数据文件所以不用担心数据丢失。这里有个关键认知MySQL 8.0中authentication_string字段保存的是一段以$A$开头的完整哈希结构不是明文也不是旧版的*开头的40位十六进制字符串。很多人直接在UPDATE mysql.user SET authentication_string123456 WHERE Userroot;结果重启后登录直接失败因为MySQL会把这段字符串当成非法哈希。这个坑后面会详细说。1.2 两条主流技术路线怎么选目前主流方案有两条skip-grant-tables和init-file。skip-grant-tables的原理是让MySQL启动时跳过授权表的加载和校验所有客户端进来都不检查密码。操作者需要以这种方式启动数据库登进去执行FLUSH PRIVILEGES让授权表生效再ALTER USER改密码。优点是通用性强、资料多、社群经验丰富缺点是启动期间数据库完全没有认证保护如果忘了加上skip-networking只监听本机局域网内任何人都能连上来风险很大。init-file是MySQL 8.0.21之后官方推荐的方案。它允许你在启动时指定一个SQL文件MySQL在初始化阶段以最高权限执行文件里的语句执行完成后数据库正常启动。这种方式不需要跳过权限表安全性明显更好尤其适合有严格生产审计要求的环境。选哪条路取决于你的场景手忙脚乱的深夜、手边只有旧笔记skip-grant-tables最稳如果是生产环境、有DB运维规范优先init-file。1.3 动手前必须确认的三件事第一确认版本。执行mysql --version输出里带MariaDB字样的用MariaDB的思路其他按MySQL处理。MySQL 5.7、8.0、8.4之间的启动参数和密码字段细节不完全一样。第二确认平台。Linux服务器多数用systemctl管理服务Windows则涉及服务管理器、my.ini或命令行前台启动操作路径差异很大。第三确认数据目录位置。重置密码时如果操作不当启动失败需要立刻检查数据目录权限和错误日志。Linux默认数据目录是/var/lib/mysqlWindows不一定要看安装路径下的my.ini里datadir配置。这一步在动手前花两分钟确认后面能少踩一半的坑。2. 方案一实践skip-grant-tables 跳过权限表重置密码2.1 Linux systemd 环境完整操作流程第一步停掉MySQL服务systemctl stop mysqld # 或者有些发行版叫 mysqlMariaDB 则是 systemctl stop mariadb # 查看服务状态确认真的停了 systemctl status mysqld第二步修改配置文件/etc/my.cnf在[mysqld]段落末尾追加两行[mysqld] skip-grant-tables skip-networking改动完成后再启动服务systemctl start mysqld此时MySQL会跳过授权表同时因为skip-networking只监听本地socket通信。执行以下命令不用密码就能登进去mysql -uroot这里极大概率会出一个问题socket文件路径不对报错ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock。解决方法是显式指定socket路径先看配置文件里的socket参数再执行mysql -uroot -S /var/lib/mysql/mysql.sock登录成功后执行下面两条SQLFLUSH PRIVILEGES; ALTER USER rootlocalhost IDENTIFIED BY 你的新密码;修改完成后重启服务注意顺序先把my.cnf里的skip-grant-tables和skip-networking删掉或注释再执行systemctl restart mysqld最后用新密码验证登录mysql -uroot -p2.2 Windows 环境的两种操作方法Windows下停止MySQL服务相对麻烦一点。先打开“服务”管理器找到MySQL对应的服务右键停止或者用管理员身份的cmd执行net stop mysql服务名不一定是mysql以实际安装为准。第一种方法最省事修改MySQL安装目录下的my.ini有的版本叫my.cnf同样在[mysqld]段下追加[mysqld] skip-grant-tables skip-networking保存后重启MySQL服务然后执行mysql -uroot -P 3306Windows没有Unix socketMySQL走TCP连接所以-P 3306在这里是必要的如果改过端口记得替换。之后同样执行FLUSH PRIVILEGES和ALTER USER。第二种方法配置文件权限不对改不了的时候用命令行前台启动。先停服务然后以管理员身份打开cmd进入MySQL安装目录的bin目录执行mysqld --skip-grant-tables --skip-networking这个窗口保持开着不要关闭再另外开一个cmd窗口执行mysql -uroot -P 3306免密登录改完密码后回到第一个窗口按CtrlC停掉mysqld进程再正常启动服务。Windows下经常遇到的一个问题是mysqld前台启动闪退这十有八九是数据目录被之前的服务进程占用或者my.ini里有其他不兼容参数。先确认服务真的停了再看data目录下有没有*.err日志文件里面会写明原因。2.3 MySQL 8.0.21 之后的一个特殊变化MySQL 8.0.21开始如果你只指定--skip-grant-tables而不加--skip-networking会发现MySQL根本没有跳过权限表登录照样报Access denied。这是官方有意调整的行为单纯skip-grant-tables在8.0.21之后不再加载必须同时指定skip-networking才生效。所以上面所有操作里skip-networking不只是一个安全加固项而是在MySQL 8.0.21之后变成了必填项。如果你用的是旧版MySQL不加skip-networking也能跳过权限表但为了安全我强烈建议还是加上。这里顺便说一个很多人会忽略的安全常识skip-grant-tables模式启动期间MySQL完全没有认证保护如果不加skip-networking只要3306端口暴露在公网或内网任何人都能直接免密连上你的数据库。所以这个模式下一定要配skip-networking并且操作完成后立刻恢复配置、重启服务。2.4 重置成功后如何恢复正常启动很多新手在改完密码后会犯一个经典错误直接在skip-grant-tables模式下用systemctl restart mysqld重启服务结果服务起不来或者起来后发现密码根本没改生效。正确顺序是这样的先用新密码验证能登录确认没问题。把my.cnf或my.ini里追加的skip-grant-tables和skip-networking两行删除。执行systemctl restart mysqld或Windows下重启服务。用mysql -uroot -p验证新密码。如果重启后登录提示密码错误先排查是不是配置文件里漏删了某一行再检查是不是改了rootlocalhost但实际程序连的是root%账号。两个账号是独立密码这个后面会详细讲。3. 方案二实践init-file 初始化文件重置密码3.1 init-file 的原理与适用场景init-file的原理特别简单MySQL启动时在授权表加载完成、网络端口监听之前会读取--init-file参数指定的SQL文件以高权限逐条执行里面的语句执行完成后再继续正常启动。因为在网络监听之前执行所以不需要跳过权限表也不会出现数据库裸奔的情况。适用场景很清晰生产环境、有时间写SQL文件、不希望数据库出现“无认证窗口期”的情况。另外如果你之前按skip-grant-tables方案改密码时因为密码策略、插件等问题反复碰壁init-file也是一个更干净的备选方案。用init-file有个前提服务器上能创建文件且MySQL进程有权读取Linux和Windows都支持但路径写法稍有差异。3.2 标准操作步骤与命令参数假设我们要把root密码改成NewPssw0rd2024。第一步停止MySQL服务systemctl stop mysqld第二步创建一个重置密码的SQL文件注意文件权限要能让MySQL进程读取到。推荐放在/tmp或MySQL数据目录下Linux上放/tmp最方便vim /tmp/reset_pwd.sql文件内容写ALTER USER rootlocalhost IDENTIFIED BY NewPssw0rd2024;第三步用--init-file参数启动MySQL。这里有个细节建议用mysqld前台启动方便观察启动日志和报错mysqld --usermysql --init-file/tmp/reset_pwd.sql --console如果启动过程没有报错而且这个SQL语句执行成功了MySQL会正常完成启动流程。看到ready for connections字样后再开一个终端窗口验证mysql -uroot -pNewPssw0rd2024验证成功后停掉MySQL把修改过的启动参数复原恢复正常启动。如果你是用systemctl管理的最稳的方式是把--init-file/tmp/reset_pwd.sql加到/etc/my.cnf的[mysqld]段执行systemctl start mysqld确认密码生效后立刻删掉这一行再systemctl restart mysqld。Windows下道理一样把--init-fileC:/tmp/reset_pwd.sql写到my.ini的[mysqld]段注意路径里的分隔符建议用正斜杠。3.3 init-file 与 skip-grant-tables 怎么取舍我个人的经验是能写SQL文件的环境优先用init-file因为它不会出现认证空窗期流程也更简洁。但如果你的MySQL版本比较老、官方文档里init-file支持和你的版本有兼容性问题或者你手头连配置文件都找不到在哪skip-grant-tables反而更直接。还有一个差异点skip-grant-tables模式下执行ALTER USER密码策略组件可能没有被加载所以哪怕是弱密码也能设置成功而init-file执行时是在正常授权流程里如果新密码不符合validate_password策略要求SQL执行会失败服务能起来但密码没改成。这个坑我在MySQL 8.0上踩过一次当时在init文件里写了个简单密码登录一直失败后来才发现是策略拦截。现在我的习惯是init-file里先写一个绝对满足复杂度要求的强密码重置成功后再登录改成符合业务要求的密码。另外补充一句init-file执行SQL文件时的报错信息一般会写进MySQL错误日志路径默认在数据目录下文件名类似主机名.err。执行失败时先翻日志别反复重启猜测。4. MySQL 8 和 MariaDB 的差异化细节4.1 密码字段的版本差异Password 还是 authentication_stringMySQL 5.6及更早版本密码哈希存放在mysql.user表的Password列。从5.7开始这个列被弃用改由authentication_string列承载到了8.0里Password列彻底废弃。所以网上很多旧教程里写的UPDATE mysql.user SET PasswordPASSWORD(xxx)在MySQL 8.0里执行会直接报错因为表结构里已经没有可用的Password字段。MySQL 8.0里修改密码最安全的写法是ALTER USER它会帮你正确生成哈希并写入对应字段。如果一定要用UPDATE也只能写SET authentication_string先清空密码哈希然后重启后正常登录再ALTER USER绝对不能把明文或随意的字符串直接写进authentication_string。前面提到过8.0的哈希是$A$开头的一段结构手写不出来的。MariaDB这边情况又不一样它保留了Password列且仍然兼容PASSWORD()函数。所以MariaDB里用UPDATE mysql.user SET PasswordPASSWORD(新密码) WHERE Userroot;是能生效的。如果记不清自己装的是哪个先执行mysql --version看到MariaDB字样就按MariaDB来。4.2 MariaDB 重置密码的细节差异MariaDB整体流程和MySQL一致也是skip-grant-tables无密码登录再清空或重置密码。差异在两点第一服务名多半叫mariadb而不是mysqldLinux上停止和启动对应的是systemctl stop mariadb、systemctl start mariadb。第二如果用的是ALTER USER rootlocalhost IDENTIFIED BY 新密码;在MariaDB 10.4之后也能正常工作。但要注意MariaDB的mysql.user表结构里Password列依然存在某些工具或脚本可能读的还是这一列。MariaDB还有一个特有情况如果你之前是用unix_socket认证插件创建的root账号免密登录应该是默认就支持的不需要skip-grant-tables。判断方法是登录后执行SELECT user, host, plugin FROM mysql.user WHERE userroot;如果plugin列显示unix_socket说明root账号走的是系统用户认证只要当前Linux用户是root就能直接mysql -uroot登录不用改密码。这种情况你连重置都不需要直接进去ALTER USER换成密码登录即可。4.3 多实例和 Docker 容器场景怎么处理多实例部署mysqld_multi时文件路径、服务名、端口都会区分多个实例改配置前先确认当前操作的是哪个实例的my.cnf避免改了A实例的配置重启B实例却把A给带崩了。Docker容器场景相对特殊。如果是用docker exec进容器docker exec -it mysql容器名 bash容器内执行mysql -uroot有时候因为容器的启动命令带了一堆环境变量root的认证方式可能没有socket插件直接无密码进不去。这时候最稳妥的做法是修改容器启动方式在docker run或docker-compose.yml里给MySQL服务加command参数追加--skip-grant-tables --skip-networking重启容器后进容器执行mysql -uroot就能登录。改完密码后记得把启动参数还原再重启。如果是数据卷挂载的容器整个重置过程不会影响业务数据但操作前最好确认数据卷没有被其他容器占用避免MySQL启动时数据目录锁冲突导致闪退。5. 常见问题速查与避坑经验5.1 高频报错排查对照表报错信息可能原因快速解决方案ERROR 1045 (28000): Access denied for user rootlocalhost密码错误或改错了host账号确认root%和rootlocalhost是不同账号分开改Cant connect to local MySQL server through socket /tmp/mysql.socksocket路径不对看my.cnf里的socket参数用-S /实际路径连接ERROR 2002 (HY000): Cant connect to local MySQL server服务没启动或启动失败看错误日志/var/log/mysqld.log或数据目录下*.errALTER USER ... Your password does not satisfy the current policy requirements密码不符合validate_password策略改为复杂密码或先SET GLOBAL validate_password.policyLOWUnknown system variable validate_password_policyMySQL 8.0变量命名方式变化改用SET GLOBAL validate_password.policyLOWMySQL 8.0.21 加了skip-grant-tables仍无法免密登录少了skip-networking参数同时加--skip-networking重启ERROR 1819出现在init-file执行时密码策略拦截换复杂密码或临时调低策略Windows mysqld前台启动闪退数据目录被占用或配置不合法确认服务已停查看*.err日志5.2 我在实际工作中踩过的几个坑第一个坑也是最典型的改完密码后忘了把配置里的skip-grant-tables删掉。当时改完密码、验证能登录就直接交付给业务方。第二天凌晨业务报警说数据库连不上或者任何机器都能无密码连进来一查才发现配置文件里那两行还在服务一直处于无认证状态。从那以后我养成了习惯修改完配置重启后第一件事就是执行mysql -uroot -p用密码登录同时执行SHOW VARIABLES LIKE skip_grant_tables;确认输出是OFF。这一步两秒钟能避免后续无数麻烦。第二个坑root%和rootlocalhost的密码不是同一个。一次客户说他改了root密码但应用还是连接失败排查到最后发现应用连的是root%而他在重置时只改了rootlocalhost。MySQL把用户名和主机组合成账号主体两个host是不同的账号密码也各自独立。所以改密码前先执行SELECT user, host, authentication_string FROM mysql.user WHERE userroot;看清楚环境里到底有几个root账号逐个改。第三个坑在skip-grant-tables模式下动了业务数据。很多人觉得“反正都进来了”顺手把线上数据库里某个字段改了或删了。但这一刻MySQL是没有权限校验的普通用户也可以操作所有库表一旦误操作没有权限保护兜底后果比忘记密码严重得多。我的原则是这个模式下只允许执行跟恢复认证相关的SQL其他一律不做。5.3 重置完成后的安全与规范化建议密码恢复不是终点接下来三件事建议一次性做掉。第一检查my.cnf或my.ini里是否残留skip-grant-tables、skip-networking、init-file相关配置。这个前面反复强调是“完成即删、删完即重启、重启即验证”的闭环。第二更新密码管理记录。团队协作环境中密码被一个人改了导致其他人登不进去往往不是技术问题而是信息同步问题。把密码轮换记录写进你们的内部文档或密码管理工具避免下次用旧密码登录报错又折腾一遍。第三确认root账号的登录限制。root账号默认只允许从localhost登录如果你发现mysql.user表里存在root%且业务并不需要远程root访问建议直接删除多余账号。远程运维用专用账号加host白名单更安全root只保留本机登录权限就够了。最后再分享一个小经验如果你运维的机器比较多建议定期演练一遍这个重置流程别等到密码真的丢了才第一次操作。把重置步骤写成运维手册标注好版本差异、配置文件路径、数据目录位置下次无论谁遇到这个问题照着流程走十分钟就能解决不用再大半夜翻文档。
返回列表